// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/*
 *******************************************************************************
 * Copyright (C) 2000-2016, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 */
package com.ibm.icu.dev.test.calendar;

import com.ibm.icu.dev.test.CoreTestFmwk;
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.text.SimpleDateFormat;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.GregorianCalendar;
import com.ibm.icu.util.HebrewCalendar;
import com.ibm.icu.util.IslamicCalendar;
import com.ibm.icu.util.SimpleTimeZone;
import com.ibm.icu.util.TimeZone;
import com.ibm.icu.util.ULocale;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.MissingResourceException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/**
 * @test 1.32 99/11/14
 * @bug 4031502 4035301 4040996 4051765 4059654 4061476 4070502 4071197 4071385 4073929 4083167
 *     4086724 4092362 4095407 4096231 4096539 4100311 4103271 4106136 4108764 4114578 4118384
 *     4125881 4125892 4136399 4141665 4142933 4145158 4145983 4147269 4149677 4162587 4165343
 *     4166109 4167060 4173516 4174361 4177484 4197699 4209071 4288792
 */
@RunWith(JUnit4.class)
public class CalendarRegressionTest extends CoreTestFmwk {
    static final String[] FIELD_NAME = {
        "ERA",
        "YEAR",
        "MONTH",
        "WEEK_OF_YEAR",
        "WEEK_OF_MONTH",
        "DAY_OF_MONTH",
        "DAY_OF_YEAR",
        "DAY_OF_WEEK",
        "DAY_OF_WEEK_IN_MONTH",
        "AM_PM",
        "HOUR",
        "HOUR_OF_DAY",
        "MINUTE",
        "SECOND",
        "MILLISECOND",
        "ZONE_OFFSET",
        "DST_OFFSET",
        "YEAR_WOY",
        "DOW_LOCAL",
        "EXTENDED_YEAR",
        "JULIAN_DAY",
        "MILLISECONDS_IN_DAY",
        "IS_LEAP_YEAR",
        "ORDINAL_MONTH"
    };

    /*
    Synopsis: java.sql.Timestamp constructor works wrong on Windows 95

    ==== Here is the test ====
    public static void main (String args[]) {
      java.sql.Timestamp t= new java.sql.Timestamp(0,15,5,5,8,13,123456700);
      logln("expected=1901-04-05 05:08:13.1234567");
      logln(" result="+t);
    }

    ==== Here is the output of the test on Solaris or NT ====
    expected=1901-04-05 05:08:13.1234567
    result=1901-04-05 05:08:13.1234567

    ==== Here is the output of the test on Windows95 ====
    expected=1901-04-05 05:08:13.1234567
    result=1901-04-05 06:08:13.1234567
    */

    @Test
    public void Test4031502() {
        try {
            // This bug actually occurs on Windows NT as well, and doesn't
            // require the host zone to be set; it can be set in Java.
            String[] ids = TimeZone.getAvailableIDs();
            boolean bad = false;
            for (int i = 0; i < ids.length; ++i) {
                TimeZone zone = TimeZone.getTimeZone(ids[i]);
                GregorianCalendar cal = new GregorianCalendar(zone);
                cal.clear();
                cal.set(1900, 15, 5, 5, 8, 13);
                if (cal.get(Calendar.HOUR) != 5) {
                    logln(
                            "Fail: "
                                    + zone.getID()
                                    + " "
                                    + zone.useDaylightTime()
                                    + "; DST_OFFSET = "
                                    + cal.get(Calendar.DST_OFFSET) / (60 * 60 * 1000.0)
                                    + "; ZONE_OFFSET = "
                                    + cal.get(Calendar.ZONE_OFFSET) / (60 * 60 * 1000.0)
                                    + "; getRawOffset() = "
                                    + zone.getRawOffset() / (60 * 60 * 1000.0)
                                    + "; HOUR = "
                                    + cal.get(Calendar.HOUR));
                    cal.clear();
                    cal.set(1900, 15, 5, 5, 8, 13);
                    if (cal.get(Calendar.HOUR) != 5) {
                        logln(
                                "Fail: "
                                        + zone.getID()
                                        + " "
                                        + zone.useDaylightTime()
                                        + "; DST_OFFSET = "
                                        + cal.get(Calendar.DST_OFFSET) / (60 * 60 * 1000.0)
                                        + "; ZONE_OFFSET = "
                                        + cal.get(Calendar.ZONE_OFFSET) / (60 * 60 * 1000.0)
                                        + "; getRawOffset() = "
                                        + zone.getRawOffset() / (60 * 60 * 1000.0)
                                        + "; HOUR = "
                                        + cal.get(Calendar.HOUR));
                        cal.clear();
                        cal.set(1900, 15, 5, 5, 8, 13);
                        logln("ms = " + cal.getTime() + " (" + cal.getTime().getTime() + ")");
                        cal.get(Calendar.HOUR);
                        java.util.GregorianCalendar cal2 =
                                new java.util.GregorianCalendar(
                                        java.util.TimeZone.getTimeZone(ids[i]));
                        cal2.clear();
                        cal2.set(1900, 15, 5, 5, 8, 13);
                        cal2.get(Calendar.HOUR);
                        logln(
                                "java.util.GC: "
                                        + zone.getID()
                                        + " "
                                        + zone.useDaylightTime()
                                        + "; DST_OFFSET = "
                                        + cal2.get(Calendar.DST_OFFSET) / (60 * 60 * 1000.0)
                                        + "; ZONE_OFFSET = "
                                        + cal2.get(Calendar.ZONE_OFFSET) / (60 * 60 * 1000.0)
                                        + "; getRawOffset() = "
                                        + zone.getRawOffset() / (60 * 60 * 1000.0)
                                        + "; HOUR = "
                                        + cal.get(Calendar.HOUR));
                        logln("ms = " + cal2.getTime() + " (" + cal2.getTime().getTime() + ")");
                        bad = true;
                    } else if (false) { // Change to true to debug
                        logln(
                                "OK: "
                                        + zone.getID()
                                        + " "
                                        + zone.useDaylightTime()
                                        + " "
                                        + cal.get(Calendar.DST_OFFSET) / (60 * 60 * 1000)
                                        + " "
                                        + zone.getRawOffset() / (60 * 60 * 1000)
                                        + ": HOUR = "
                                        + cal.get(Calendar.HOUR));
                    }
                }
                if (bad) errln("TimeZone problems with GC");
            }
        } catch (MissingResourceException e) {
            warnln("Could not load data. " + e.getMessage());
        }
    }

    @Test
    public void Test4035301() {

        try {
            GregorianCalendar c = new GregorianCalendar(98, 8, 7);
            GregorianCalendar d = new GregorianCalendar(98, 8, 7);
            if (c.after(d)
                    || c.after(c)
                    || c.before(d)
                    || c.before(c)
                    || !c.equals(c)
                    || !c.equals(d)) errln("Fail");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            warnln("Could not load data. " + e.getMessage());
        }
    }

    @Test
    public void Test4040996() {
        try {
            String[] ids = TimeZone.getAvailableIDs(-8 * 60 * 60 * 1000);
            SimpleTimeZone pdt = new SimpleTimeZone(-8 * 60 * 60 * 1000, ids[0]);
            pdt.setStartRule(Calendar.APRIL, 1, Calendar.SUNDAY, 2 * 60 * 60 * 1000);
            pdt.setEndRule(Calendar.OCTOBER, -1, Calendar.SUNDAY, 2 * 60 * 60 * 1000);
            Calendar calendar = new GregorianCalendar(pdt);

            calendar.set(Calendar.MONTH, 3);
            calendar.set(Calendar.DAY_OF_MONTH, 18);
            calendar.set(Calendar.SECOND, 30);

            logln("MONTH: " + calendar.get(Calendar.MONTH));
            logln("DAY_OF_MONTH: " + calendar.get(Calendar.DAY_OF_MONTH));
            logln("MINUTE: " + calendar.get(Calendar.MINUTE));
            logln("SECOND: " + calendar.get(Calendar.SECOND));

            calendar.add(Calendar.SECOND, 6);
            // This will print out todays date for MONTH and DAY_OF_MONTH
            // instead of the date it was set to.
            // This happens when adding MILLISECOND or MINUTE also
            logln("MONTH: " + calendar.get(Calendar.MONTH));
            logln("DAY_OF_MONTH: " + calendar.get(Calendar.DAY_OF_MONTH));
            logln("MINUTE: " + calendar.get(Calendar.MINUTE));
            logln("SECOND: " + calendar.get(Calendar.SECOND));
            if (calendar.get(Calendar.MONTH) != 3
                    || calendar.get(Calendar.DAY_OF_MONTH) != 18
                    || calendar.get(Calendar.SECOND) != 36) errln("Fail: Calendar.add misbehaves");
        } catch (Exception e) {
            warnln("Could not load data. " + e.getMessage());
        }
    }

    @Test
    public void Test4051765() {
        try {
            Calendar cal = Calendar.getInstance();
            cal.setLenient(false);
            cal.set(Calendar.DAY_OF_WEEK, 0);
            try {
                cal.getTime();
                errln("Fail: DAY_OF_WEEK 0 should be disallowed");
            } catch (IllegalArgumentException e) {
                return;
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            warnln("Could not load data. " + e.getMessage());
        }
    }

    /*
     * User error - no bug here public void Test4059524() { // Create calendar
     * for April 10, 1997 GregorianCalendar calendar = new GregorianCalendar(); //
     * print out a bunch of interesting things logln("ERA: " +
     * calendar.get(calendar.ERA)); logln("YEAR: " +
     * calendar.get(calendar.YEAR)); logln("MONTH: " +
     * calendar.get(calendar.MONTH)); logln("WEEK_OF_YEAR: " +
     * calendar.get(calendar.WEEK_OF_YEAR)); logln("WEEK_OF_MONTH: " +
     * calendar.get(calendar.WEEK_OF_MONTH)); logln("DATE: " +
     * calendar.get(calendar.DATE)); logln("DAY_OF_MONTH: " +
     * calendar.get(calendar.DAY_OF_MONTH)); logln("DAY_OF_YEAR: " +
     * calendar.get(calendar.DAY_OF_YEAR)); logln("DAY_OF_WEEK: " +
     * calendar.get(calendar.DAY_OF_WEEK)); logln("DAY_OF_WEEK_IN_MONTH: " +
     * calendar.get(calendar.DAY_OF_WEEK_IN_MONTH)); logln("AM_PM: " +
     * calendar.get(calendar.AM_PM)); logln("HOUR: " +
     * calendar.get(calendar.HOUR)); logln("HOUR_OF_DAY: " +
     * calendar.get(calendar.HOUR_OF_DAY)); logln("MINUTE: " +
     * calendar.get(calendar.MINUTE)); logln("SECOND: " +
     * calendar.get(calendar.SECOND)); logln("MILLISECOND: " +
     * calendar.get(calendar.MILLISECOND)); logln("ZONE_OFFSET: " +
     * (calendar.get(calendar.ZONE_OFFSET)/(60*60*1000))); logln("DST_OFFSET: " +
     * (calendar.get(calendar.DST_OFFSET)/(60*60*1000))); calendar = new
     * GregorianCalendar(1997,3,10); calendar.getTime(); logln("April 10,
     * 1997"); logln("ERA: " + calendar.get(calendar.ERA)); logln("YEAR: " +
     * calendar.get(calendar.YEAR)); logln("MONTH: " +
     * calendar.get(calendar.MONTH)); logln("WEEK_OF_YEAR: " +
     * calendar.get(calendar.WEEK_OF_YEAR)); logln("WEEK_OF_MONTH: " +
     * calendar.get(calendar.WEEK_OF_MONTH)); logln("DATE: " +
     * calendar.get(calendar.DATE)); logln("DAY_OF_MONTH: " +
     * calendar.get(calendar.DAY_OF_MONTH)); logln("DAY_OF_YEAR: " +
     * calendar.get(calendar.DAY_OF_YEAR)); logln("DAY_OF_WEEK: " +
     * calendar.get(calendar.DAY_OF_WEEK)); logln("DAY_OF_WEEK_IN_MONTH: " +
     * calendar.get(calendar.DAY_OF_WEEK_IN_MONTH)); logln("AM_PM: " +
     * calendar.get(calendar.AM_PM)); logln("HOUR: " +
     * calendar.get(calendar.HOUR)); logln("HOUR_OF_DAY: " +
     * calendar.get(calendar.HOUR_OF_DAY)); logln("MINUTE: " +
     * calendar.get(calendar.MINUTE)); logln("SECOND: " +
     * calendar.get(calendar.SECOND)); logln("MILLISECOND: " +
     * calendar.get(calendar.MILLISECOND)); logln("ZONE_OFFSET: " +
     * (calendar.get(calendar.ZONE_OFFSET)/(60*60*1000))); // in hours
     * logln("DST_OFFSET: " + (calendar.get(calendar.DST_OFFSET)/(60*60*1000))); //
     * in hours }
     */

    @Test
    public void Test4059654() {
        //     try {
        // work around bug for jdk1.4 on solaris 2.6, which uses funky
        // timezone names
        // jdk1.4.1 will drop support for 2.6 so we should be ok when it
        // comes out
        java.util.TimeZone javazone = java.util.TimeZone.getTimeZone("GMT");
        TimeZone icuzone = TimeZone.getTimeZone("GMT");

        GregorianCalendar gc = new GregorianCalendar(icuzone);

        gc.set(1997, 3, 1, 15, 16, 17); // April 1, 1997

        gc.set(Calendar.HOUR, 0);
        gc.set(Calendar.AM_PM, Calendar.AM);
        gc.set(Calendar.MINUTE, 0);
        gc.set(Calendar.SECOND, 0);
        gc.set(Calendar.MILLISECOND, 0);

        Date cd = gc.getTime();
        java.util.Calendar cal = java.util.Calendar.getInstance(javazone);
        cal.clear();
        cal.set(1997, 3, 1, 0, 0, 0);
        Date exp = cal.getTime();
        if (!cd.equals(exp)) errln("Fail: Calendar.set broken. Got " + cd + " Want " + exp);
        //     } catch (RuntimeException e) {
        // TODO Auto-generated catch block
        //         e.printStackTrace();
        //      }
    }

    @Test
    public void Test4061476() {
        SimpleDateFormat fmt = new SimpleDateFormat("ddMMMyy", Locale.UK);
        Calendar cal = GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"), Locale.UK);
        fmt.setCalendar(cal);
        try {
            Date date = fmt.parse("29MAY97");
            cal.setTime(date);
        } catch (Exception e) {
            System.out.print("");
        }
        cal.set(Calendar.HOUR_OF_DAY, 13);
        logln("Hour: " + cal.get(Calendar.HOUR_OF_DAY));
        cal.add(Calendar.HOUR_OF_DAY, 6);
        logln("Hour: " + cal.get(Calendar.HOUR_OF_DAY));
        if (cal.get(Calendar.HOUR_OF_DAY) != 19)
            errln("Fail: Want 19 Got " + cal.get(Calendar.HOUR_OF_DAY));
    }

    @Test
    public void Test4070502() {
        java.util.Calendar tempcal = java.util.Calendar.getInstance();
        tempcal.clear();
        tempcal.set(1998, 0, 30);
        Date d = getAssociatedDate(tempcal.getTime());
        Calendar cal = new GregorianCalendar();
        cal.setTime(d);
        if (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY
                || cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY)
            errln("Fail: Want weekday Got " + d);
    }

    /**
     * Get the associated date starting from a specified date NOTE: the unnecessary "getTime()'s"
     * below are a work-around for a bug in jdk 1.1.3 (and probably earlier versions also)
     *
     * <p>
     *
     * @param d The date to start from
     */
    public static Date getAssociatedDate(Date d) {
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(d);
        // cal.add(field, amount); //<-- PROBLEM SEEN WITH field = DATE,MONTH
        // cal.getTime(); // <--- REMOVE THIS TO SEE BUG
        while (true) {
            int wd = cal.get(Calendar.DAY_OF_WEEK);
            if (wd == Calendar.SATURDAY || wd == Calendar.SUNDAY) {
                cal.add(Calendar.DATE, 1);
                // cal.getTime();
            } else break;
        }
        return cal.getTime();
    }

    @Test
    public void Test4071197() {
        dowTest(false);
        dowTest(true);
    }

    void dowTest(boolean lenient) {
        GregorianCalendar cal = new GregorianCalendar();
        cal.set(1997, Calendar.AUGUST, 12); // Wednesday
        // cal.getTime(); // Force update
        cal.setLenient(lenient);
        cal.set(1996, Calendar.DECEMBER, 1); // Set the date to be December 1,
        // 1996
        int dow = cal.get(Calendar.DAY_OF_WEEK);
        int min = cal.getMinimum(Calendar.DAY_OF_WEEK);
        int max = cal.getMaximum(Calendar.DAY_OF_WEEK);
        logln(cal.getTime().toString());
        if (min != Calendar.SUNDAY || max != Calendar.SATURDAY) errln("FAIL: Min/max bad");
        if (dow < min || dow > max) errln("FAIL: Day of week " + dow + " out of range");
        if (dow != Calendar.SUNDAY) errln("FAIL: Day of week should be SUNDAY Got " + dow);
    }

    @Test
    public void Test4071385() {
        // work around bug for jdk1.4 on solaris 2.6, which uses funky timezone
        // names
        // jdk1.4.1 will drop support for 2.6 so we should be ok when it comes out
        java.util.TimeZone javazone = java.util.TimeZone.getTimeZone("GMT");
        TimeZone icuzone = TimeZone.getTimeZone("GMT");

        Calendar cal = Calendar.getInstance(icuzone);
        java.util.Calendar tempcal = java.util.Calendar.getInstance(javazone);
        tempcal.clear();
        tempcal.set(1998, Calendar.JUNE, 24);
        cal.setTime(tempcal.getTime());
        cal.set(Calendar.MONTH, Calendar.NOVEMBER); // change a field
        logln(cal.getTime().toString());
        tempcal.set(1998, Calendar.NOVEMBER, 24);
        if (!cal.getTime().equals(tempcal.getTime())) errln("Fail");
    }

    @Test
    public void Test4073929() {
        GregorianCalendar foo1 = new GregorianCalendar(1997, 8, 27);
        foo1.add(Calendar.DAY_OF_MONTH, +1);
        int testyear = foo1.get(Calendar.YEAR);
        int testmonth = foo1.get(Calendar.MONTH);
        int testday = foo1.get(Calendar.DAY_OF_MONTH);
        if (testyear != 1997 || testmonth != 8 || testday != 28)
            errln("Fail: Calendar not initialized");
    }

    @Test
    public void Test4083167() {
        TimeZone saveZone = TimeZone.getDefault();
        try {
            TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
            Date firstDate = new Date();
            Calendar cal = new GregorianCalendar();
            cal.setTime(firstDate);
            long firstMillisInDay =
                    cal.get(Calendar.HOUR_OF_DAY) * 3600000L
                            + cal.get(Calendar.MINUTE) * 60000L
                            + cal.get(Calendar.SECOND) * 1000L
                            + cal.get(Calendar.MILLISECOND);

            logln("Current time: " + firstDate.toString());

            for (int validity = 0; validity < 30; validity++) {
                Date lastDate =
                        new Date(firstDate.getTime() + (long) validity * 1000 * 24 * 60 * 60);
                cal.setTime(lastDate);
                long millisInDay =
                        cal.get(Calendar.HOUR_OF_DAY) * 3600000L
                                + cal.get(Calendar.MINUTE) * 60000L
                                + cal.get(Calendar.SECOND) * 1000L
                                + cal.get(Calendar.MILLISECOND);
                if (firstMillisInDay != millisInDay) errln("Day has shifted " + lastDate);
            }
        } finally {
            TimeZone.setDefault(saveZone);
        }
    }

    @Test
    public void Test4086724() {
        SimpleDateFormat date;
        TimeZone saveZone = TimeZone.getDefault();
        Locale saveLocale = Locale.getDefault();
        try {
            Locale.setDefault(Locale.UK);
            TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
            date = new SimpleDateFormat("dd MMM yyy (zzzz) 'is in week' ww");
            Calendar cal = Calendar.getInstance();
            cal.set(1997, Calendar.SEPTEMBER, 30);
            Date now = cal.getTime();
            logln(date.format(now));
            cal.set(1997, Calendar.JANUARY, 1);
            now = cal.getTime();
            logln(date.format(now));
            cal.set(1997, Calendar.JANUARY, 8);
            now = cal.getTime();
            logln(date.format(now));
            cal.set(1996, Calendar.DECEMBER, 31);
            now = cal.getTime();
            logln(date.format(now));
        } finally {
            Locale.setDefault(saveLocale);
            TimeZone.setDefault(saveZone);
        }
        logln("*** THE RESULTS OF THIS TEST MUST BE VERIFIED MANUALLY ***");
    }

    @Test
    public void Test4092362() {
        GregorianCalendar cal1 = new GregorianCalendar(1997, 10, 11, 10, 20, 40);
        /*
         * cal1.set( Calendar.YEAR, 1997 ); cal1.set( Calendar.MONTH, 10 );
         * cal1.set( Calendar.DATE, 11 ); cal1.set( Calendar.HOUR, 10 );
         * cal1.set( Calendar.MINUTE, 20 ); cal1.set( Calendar.SECOND, 40 );
         */

        logln(" Cal1 = " + cal1.getTime().getTime());
        logln(" Cal1 time in ms = " + cal1.get(Calendar.MILLISECOND));
        for (int k = 0; k < 100; k++) {
            System.out.print("");
        }

        GregorianCalendar cal2 = new GregorianCalendar(1997, 10, 11, 10, 20, 40);
        /*
         * cal2.set( Calendar.YEAR, 1997 ); cal2.set( Calendar.MONTH, 10 );
         * cal2.set( Calendar.DATE, 11 ); cal2.set( Calendar.HOUR, 10 );
         * cal2.set( Calendar.MINUTE, 20 ); cal2.set( Calendar.SECOND, 40 );
         */

        logln(" Cal2 = " + cal2.getTime().getTime());
        logln(" Cal2 time in ms = " + cal2.get(Calendar.MILLISECOND));
        if (!cal1.equals(cal2)) errln("Fail: Milliseconds randomized");
    }

    @Test
    public void Test4095407() {
        GregorianCalendar a = new GregorianCalendar(1997, Calendar.NOVEMBER, 13);
        int dow = a.get(Calendar.DAY_OF_WEEK);
        if (dow != Calendar.THURSDAY) errln("Fail: Want THURSDAY Got " + dow);
    }

    @Test
    public void Test4096231() {
        TimeZone GMT = TimeZone.getTimeZone("GMT");
        TimeZone PST = TimeZone.getTimeZone("PST");
        int sec = 0, min = 0, hr = 0, day = 1, month = 10, year = 1997;

        Calendar cal1 = new GregorianCalendar(PST);
        cal1.setTime(new Date(880698639000L));
        int p;
        logln("PST 1 is: " + (p = cal1.get(Calendar.HOUR_OF_DAY)));
        cal1.setTimeZone(GMT);
        // Issue 1: Changing the timezone doesn't change the
        //          represented time.
        int h1, h2;
        logln("GMT 1 is: " + (h1 = cal1.get(Calendar.HOUR_OF_DAY)));
        cal1.setTime(new Date(880698639000L));
        logln("GMT 2 is: " + (h2 = cal1.get(Calendar.HOUR_OF_DAY)));
        // Note: This test had a bug in it. It wanted h1!=h2, when
        // what was meant was h1!=p. Fixed this concurrent with fix
        // to 4177484.
        if (p == h1 || h1 != h2) errln("Fail: Hour same in different zones");

        Calendar cal2 = new GregorianCalendar(GMT);
        Calendar cal3 = new GregorianCalendar(PST);
        cal2.set(Calendar.MILLISECOND, 0);
        cal3.set(Calendar.MILLISECOND, 0);

        cal2.set(
                cal1.get(Calendar.YEAR),
                cal1.get(Calendar.MONTH),
                cal1.get(Calendar.DAY_OF_MONTH),
                cal1.get(Calendar.HOUR_OF_DAY),
                cal1.get(Calendar.MINUTE),
                cal1.get(Calendar.SECOND));

        long t1, t2, t3, t4;
        logln("RGMT 1 is: " + (t1 = cal2.getTime().getTime()));
        cal3.set(year, month, day, hr, min, sec);
        logln("RPST 1 is: " + (t2 = cal3.getTime().getTime()));
        cal3.setTimeZone(GMT);
        logln("RGMT 2 is: " + (t3 = cal3.getTime().getTime()));
        cal3.set(
                cal1.get(Calendar.YEAR),
                cal1.get(Calendar.MONTH),
                cal1.get(Calendar.DAY_OF_MONTH),
                cal1.get(Calendar.HOUR_OF_DAY),
                cal1.get(Calendar.MINUTE),
                cal1.get(Calendar.SECOND));
        // Issue 2: Calendar continues to use the timezone in its
        //          constructor for set() conversions, regardless
        //          of calls to setTimeZone()
        logln("RGMT 3 is: " + (t4 = cal3.getTime().getTime()));
        if (t1 == t2 || t1 != t4 || t2 != t3) errln("Fail: Calendar zone behavior faulty");
    }

    @Test
    public void Test4096539() {
        int[] y = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

        for (int x = 0; x < 12; x++) {
            GregorianCalendar gc = new GregorianCalendar(1997, x, y[x]);
            int m1, m2;
            log(
                    (m1 = gc.get(Calendar.MONTH) + 1)
                            + "/"
                            + gc.get(Calendar.DATE)
                            + "/"
                            + gc.get(Calendar.YEAR)
                            + " + 1mo = ");

            gc.add(Calendar.MONTH, 1);
            logln(
                    (m2 = gc.get(Calendar.MONTH) + 1)
                            + "/"
                            + gc.get(Calendar.DATE)
                            + "/"
                            + gc.get(Calendar.YEAR));
            int m = (m1 % 12) + 1;
            if (m2 != m) errln("Fail: Want " + m + " Got " + m2);
        }
    }

    @Test
    public void Test4100311() {
        GregorianCalendar cal = (GregorianCalendar) Calendar.getInstance();
        cal.set(Calendar.YEAR, 1997);
        cal.set(Calendar.DAY_OF_YEAR, 1);
        Date d = cal.getTime(); // Should be Jan 1
        logln(d.toString());
        if (cal.get(Calendar.DAY_OF_YEAR) != 1) errln("Fail: DAY_OF_YEAR not set");
    }

    @Test
    public void Test4103271() {
        SimpleDateFormat sdf = new SimpleDateFormat();
        int numYears = 40, startYear = 1997, numDays = 15;
        String output, testDesc;
        GregorianCalendar testCal = (GregorianCalendar) Calendar.getInstance();
        testCal.clear();
        sdf.setCalendar(testCal);
        sdf.applyPattern("d MMM yyyy");
        boolean fail = false;
        for (int firstDay = 1; firstDay <= 2; firstDay++) {
            for (int minDays = 1; minDays <= 7; minDays++) {
                testCal.setMinimalDaysInFirstWeek(minDays);
                testCal.setFirstDayOfWeek(firstDay);
                testDesc = ("Test" + String.valueOf(firstDay) + String.valueOf(minDays));
                logln(
                        testDesc
                                + " => 1st day of week="
                                + String.valueOf(firstDay)
                                + ", minimum days in first week="
                                + String.valueOf(minDays));
                for (int j = startYear; j <= startYear + numYears; j++) {
                    testCal.set(j, 11, 25);
                    for (int i = 0; i < numDays; i++) {
                        testCal.add(Calendar.DATE, 1);
                        String calWOY;
                        int actWOY = testCal.get(Calendar.WEEK_OF_YEAR);
                        if (actWOY < 1 || actWOY > 53) {
                            Date d = testCal.getTime();
                            calWOY = String.valueOf(actWOY);
                            output = testDesc + " - " + sdf.format(d) + "\t";
                            output = output + "\t" + calWOY;
                            logln(output);
                            fail = true;
                        }
                    }
                }
            }
        }

        int[] DATA = {
            3, 52, 52, 52, 52, 52, 52, 52, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
            4, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, 1, 1, 1, 1, 1, 1, 1,
        };
        testCal.setFirstDayOfWeek(Calendar.SUNDAY);
        for (int j = 0; j < DATA.length; j += 22) {
            logln("Minimal days in first week = " + DATA[j] + "  Week starts on Sunday");
            testCal.setMinimalDaysInFirstWeek(DATA[j]);
            testCal.set(1997, Calendar.DECEMBER, 21);
            for (int i = 0; i < 21; ++i) {
                int woy = testCal.get(Calendar.WEEK_OF_YEAR);
                log(testCal.getTime() + " " + woy);
                if (woy != DATA[j + 1 + i]) {
                    log(" ERROR");
                    fail = true;
                }
                // logln();

                // Now compute the time from the fields, and make sure we
                // get the same answer back. This is a round-trip test.
                Date save = testCal.getTime();
                testCal.clear();
                testCal.set(Calendar.YEAR, DATA[j + 1 + i] < 25 ? 1998 : 1997);
                testCal.set(Calendar.WEEK_OF_YEAR, DATA[j + 1 + i]);
                testCal.set(Calendar.DAY_OF_WEEK, (i % 7) + Calendar.SUNDAY);
                if (!testCal.getTime().equals(save)) {
                    logln("  Parse failed: " + testCal.getTime());
                    fail = true;
                }

                testCal.setTime(save);
                testCal.add(Calendar.DAY_OF_MONTH, 1);
            }
        }

        Date d[] = new Date[8];
        java.util.Calendar tempcal = java.util.Calendar.getInstance();
        tempcal.clear();
        tempcal.set(1997, Calendar.DECEMBER, 28);
        d[0] = tempcal.getTime();
        tempcal.set(1998, Calendar.JANUARY, 10);
        d[1] = tempcal.getTime();
        tempcal.set(1998, Calendar.DECEMBER, 31);
        d[2] = tempcal.getTime();
        tempcal.set(1999, Calendar.JANUARY, 1);
        d[3] = tempcal.getTime();
        // Test field disambiguation with a few special hard-coded cases.
        // This shouldn't fail if the above cases aren't failing.
        Object[] DISAM = {
            1998, 1, Calendar.SUNDAY, d[0],
            1998, 2, Calendar.SATURDAY, d[1],
            1998, 53, Calendar.THURSDAY, d[2],
            1998, 53, Calendar.FRIDAY, d[3],
        };
        testCal.setMinimalDaysInFirstWeek(3);
        testCal.setFirstDayOfWeek(Calendar.SUNDAY);
        for (int i = 0; i < DISAM.length; i += 4) {
            int y = ((Integer) DISAM[i]).intValue();
            int woy = ((Integer) DISAM[i + 1]).intValue();
            int dow = ((Integer) DISAM[i + 2]).intValue();
            Date exp = (Date) DISAM[i + 3];
            testCal.clear();
            testCal.set(Calendar.YEAR, y);
            testCal.set(Calendar.WEEK_OF_YEAR, woy);
            testCal.set(Calendar.DAY_OF_WEEK, dow);
            log(y + "-W" + woy + "-DOW" + dow + " expect:" + exp + " got:" + testCal.getTime());
            if (!testCal.getTime().equals(exp)) {
                log("  FAIL");
                fail = true;
            }
            // logln();
        }

        // Now try adding and rolling
        Object ADD = new Object();
        Object ROLL = new Object();
        tempcal.set(1998, Calendar.DECEMBER, 25);
        d[0] = tempcal.getTime();
        tempcal.set(1999, Calendar.JANUARY, 1);
        d[1] = tempcal.getTime();
        tempcal.set(1997, Calendar.DECEMBER, 28);
        d[2] = tempcal.getTime();
        tempcal.set(1998, Calendar.JANUARY, 4);
        d[3] = tempcal.getTime();
        tempcal.set(1998, Calendar.DECEMBER, 27);
        d[4] = tempcal.getTime();
        tempcal.set(1997, Calendar.DECEMBER, 28);
        d[5] = tempcal.getTime();
        tempcal.set(1999, Calendar.JANUARY, 2);
        d[6] = tempcal.getTime();
        tempcal.set(1998, Calendar.JANUARY, 3);
        d[7] = tempcal.getTime();

        Object[] ADDROLL = {
            ADD, 1, d[0], d[1],
            ADD, 1, d[2], d[3],
            ROLL, 1, d[4], d[5],
            ROLL, 1, d[6], d[7],
        };
        testCal.setMinimalDaysInFirstWeek(3);
        testCal.setFirstDayOfWeek(Calendar.SUNDAY);
        for (int i = 0; i < ADDROLL.length; i += 4) {
            int amount = ((Integer) ADDROLL[i + 1]).intValue();
            Date before = (Date) ADDROLL[i + 2];
            Date after = (Date) ADDROLL[i + 3];

            testCal.setTime(before);
            if (ADDROLL[i] == ADD) testCal.add(Calendar.WEEK_OF_YEAR, amount);
            else testCal.roll(Calendar.WEEK_OF_YEAR, amount);
            log(
                    (ADDROLL[i] == ADD ? "add(WOY," : "roll(WOY,")
                            + amount
                            + ") "
                            + before
                            + " => "
                            + testCal.getTime());
            if (!after.equals(testCal.getTime())) {
                logln("  exp:" + after + "  FAIL");
                fail = true;
            } else logln(" ok");

            testCal.setTime(after);
            if (ADDROLL[i] == ADD) testCal.add(Calendar.WEEK_OF_YEAR, -amount);
            else testCal.roll(Calendar.WEEK_OF_YEAR, -amount);
            log(
                    (ADDROLL[i] == ADD ? "add(WOY," : "roll(WOY,")
                            + (-amount)
                            + ") "
                            + after
                            + " => "
                            + testCal.getTime());
            if (!before.equals(testCal.getTime())) {
                logln("  exp:" + before + "  FAIL");
                fail = true;
            } else logln(" ok");
        }

        if (fail) errln("Fail: Week of year misbehaving");
    }

    @Test
    public void Test4106136() {
        Locale saveLocale = Locale.getDefault();
        String[] names = {"Calendar", "DateFormat", "NumberFormat"};
        try {
            Locale[] locales = {Locale.CHINESE, Locale.CHINA};
            for (int i = 0; i < locales.length; ++i) {
                Locale.setDefault(locales[i]);
                int[] n = {
                    Calendar.getAvailableLocales().length,
                    DateFormat.getAvailableLocales().length,
                    NumberFormat.getAvailableLocales().length
                };
                for (int j = 0; j < n.length; ++j) {
                    if (n[j] == 0) errln("Fail: " + names[j] + " has no locales for " + locales[i]);
                }
            }
        } finally {
            Locale.setDefault(saveLocale);
        }
    }

    @Test
    public void Test4108764() {
        java.util.Calendar tempcal = java.util.Calendar.getInstance();
        tempcal.clear();
        tempcal.set(1997, Calendar.FEBRUARY, 15, 12, 00, 00);
        Date d00 = tempcal.getTime();
        tempcal.set(1997, Calendar.FEBRUARY, 15, 12, 00, 56);
        Date d01 = tempcal.getTime();
        tempcal.set(1997, Calendar.FEBRUARY, 15, 12, 34, 00);
        Date d10 = tempcal.getTime();
        tempcal.set(1997, Calendar.FEBRUARY, 15, 12, 34, 56);
        Date d11 = tempcal.getTime();
        tempcal.set(1997, Calendar.JANUARY, 15, 12, 34, 56);
        Date dM = tempcal.getTime();
        tempcal.clear();
        tempcal.set(1970, Calendar.JANUARY, 1);
        Date epoch = tempcal.getTime();

        Calendar cal = Calendar.getInstance();
        cal.setTime(d11);

        cal.clear(Calendar.MINUTE);
        logln(cal.getTime().toString());
        if (!cal.getTime().equals(d01)) {
            errln("Fail: " + d11 + " clear(MINUTE) => expect " + d01 + ", got " + cal.getTime());
        }

        cal.set(Calendar.SECOND, 0);
        logln(cal.getTime().toString());
        if (!cal.getTime().equals(d00)) errln("Fail: set(SECOND, 0) broken");

        cal.setTime(d11);
        cal.set(Calendar.SECOND, 0);
        logln(cal.getTime().toString());
        if (!cal.getTime().equals(d10)) errln("Fail: set(SECOND, 0) broken #2");

        cal.clear(Calendar.MINUTE);
        logln(cal.getTime().toString());
        if (!cal.getTime().equals(d00)) errln("Fail: clear(MINUTE) broken #2");

        cal.clear();
        logln(cal.getTime().toString());
        if (!cal.getTime().equals(epoch))
            errln("Fail: after clear() expect " + epoch + ", got " + cal.getTime());

        cal.setTime(d11);
        cal.clear(Calendar.MONTH);
        logln(cal.getTime().toString());
        if (!cal.getTime().equals(dM)) {
            errln("Fail: " + d11 + " clear(MONTH) => expect " + dM + ", got " + cal.getTime());
        }
    }

    @Test
    public void Test4114578() {
        int ONE_HOUR = 60 * 60 * 1000;
        Calendar cal = Calendar.getInstance();
        cal.setTimeZone(TimeZone.getTimeZone("PST"));

        java.util.Calendar tempcal = java.util.Calendar.getInstance();
        tempcal.clear();
        tempcal.set(1998, Calendar.APRIL, 5, 1, 0);
        long onset = tempcal.getTime().getTime() + ONE_HOUR;
        tempcal.set(1998, Calendar.OCTOBER, 25, 0, 0);
        long cease = tempcal.getTime().getTime() + 2 * ONE_HOUR;

        boolean fail = false;

        final int ADD = 1;
        final int ROLL = 2;

        long[] DATA = {
            // Start Action Amt Expected_change
            onset - ONE_HOUR,
            ADD,
            1,
            ONE_HOUR,
            onset,
            ADD,
            -1,
            -ONE_HOUR,
            onset - ONE_HOUR,
            ROLL,
            1,
            ONE_HOUR,
            onset,
            ROLL,
            -1,
            -ONE_HOUR,
            cease - ONE_HOUR,
            ADD,
            1,
            ONE_HOUR,
            cease,
            ADD,
            -1,
            -ONE_HOUR,
            cease - ONE_HOUR,
            ROLL,
            1,
            ONE_HOUR,
            cease,
            ROLL,
            -1,
            -ONE_HOUR,
        };

        for (int i = 0; i < DATA.length; i += 4) {
            Date date = new Date(DATA[i]);
            int amt = (int) DATA[i + 2];
            long expectedChange = DATA[i + 3];

            log(date.toString());
            cal.setTime(date);

            switch ((int) DATA[i + 1]) {
                case ADD:
                    log(" add (HOUR," + (amt < 0 ? "" : "+") + amt + ")= ");
                    cal.add(Calendar.HOUR, amt);
                    break;
                case ROLL:
                    log(" roll(HOUR," + (amt < 0 ? "" : "+") + amt + ")= ");
                    cal.roll(Calendar.HOUR, amt);
                    break;
            }

            log(cal.getTime().toString());

            long change = cal.getTime().getTime() - date.getTime();
            if (change != expectedChange) {
                fail = true;
                logln(" FAIL");
            } else logln(" OK");
        }

        if (fail) errln("Fail: roll/add misbehaves around DST onset/cease");
    }

    /** Make sure maximum for HOUR field is 11, not 12. */
    @Test
    public void Test4118384() {
        Calendar cal = Calendar.getInstance();
        if (cal.getMaximum(Calendar.HOUR) != 11
                || cal.getLeastMaximum(Calendar.HOUR) != 11
                || cal.getActualMaximum(Calendar.HOUR) != 11)
            errln("Fail: maximum of HOUR field should be 11");
    }

    /** Check isLeapYear for BC years. */
    @Test
    public void Test4125881() {
        GregorianCalendar cal = (GregorianCalendar) Calendar.getInstance();
        DateFormat fmt = new SimpleDateFormat("MMMM d, yyyy G");
        cal.clear();
        for (int y = -20; y <= 10; ++y) {
            cal.set(Calendar.ERA, y < 1 ? GregorianCalendar.BC : GregorianCalendar.AD);
            cal.set(Calendar.YEAR, y < 1 ? 1 - y : y);
            logln(y + " = " + fmt.format(cal.getTime()) + " " + cal.isLeapYear(y));
            if (cal.isLeapYear(y) != ((y + 40) % 4 == 0)) errln("Leap years broken");
        }
    }

    // I am disabling this test -- it is currently failing because of a bug
    // in Sun's latest change to STZ.getOffset(). I have filed a Sun bug
    // against this problem.

    // Re-enabled after 'porting' TZ and STZ from java.util to com.ibm.icu.util.
    /**
     * Prove that GregorianCalendar is proleptic (it used to cut off at 45 BC, and not have leap
     * years before then).
     */
    @Test
    public void Test4125892() {
        GregorianCalendar cal = (GregorianCalendar) Calendar.getInstance();
        // DateFormat fmt = new SimpleDateFormat("MMMM d, yyyy G");
        // fmt = null;
        cal.clear();
        cal.set(Calendar.ERA, GregorianCalendar.BC);
        cal.set(Calendar.YEAR, 81); // 81 BC is a leap year (proleptically)
        cal.set(Calendar.MONTH, Calendar.FEBRUARY);
        cal.set(Calendar.DATE, 28);
        cal.add(Calendar.DATE, 1);
        if (cal.get(Calendar.DATE) != 29 || !cal.isLeapYear(-80)) // -80 == 81 BC
        errln("Calendar not proleptic");
    }

    /**
     * Calendar and GregorianCalendar hashCode() methods need improvement. Calendar needs a good
     * implementation that subclasses can override, and GregorianCalendar should use that
     * implementation.
     */
    @Test
    public void Test4136399() {
        /*
         * Note: This test is actually more strict than it has to be.
         * Technically, there is no requirement that unequal objects have
         * unequal hashes. We only require equal objects to have equal hashes.
         * It is desirable for unequal objects to have distributed hashes, but
         * there is no hard requirement here.
         *
         * In this test we make assumptions about certain attributes of calendar
         * objects getting represented in the hash, which need not always be the
         * case (although it does work currently with the given test).
         */
        Calendar a = Calendar.getInstance();
        Calendar b = a.clone();
        if (a.hashCode() != b.hashCode()) {
            errln("Calendar hash code unequal for cloned objects");
        }
        TimeZone atz1 = a.getTimeZone();
        TimeZone atz2 = atz1.clone();
        if (!atz1.equals(atz2)) {
            errln("The clone timezones are not equal");
        }
        if (atz1.hashCode() != atz2.hashCode()) {
            errln("TimeZone hash code unequal for cloned objects");
        }
        b.setMinimalDaysInFirstWeek(7 - a.getMinimalDaysInFirstWeek());
        if (a.hashCode() == b.hashCode()) {
            errln("Calendar hash code ignores minimal days in first week");
        }
        b.setMinimalDaysInFirstWeek(a.getMinimalDaysInFirstWeek());

        b.setFirstDayOfWeek((a.getFirstDayOfWeek() % 7) + 1); // Next day
        if (a.hashCode() == b.hashCode()) {
            errln("Calendar hash code ignores first day of week");
        }
        b.setFirstDayOfWeek(a.getFirstDayOfWeek());

        b.setLenient(!a.isLenient());
        if (a.hashCode() == b.hashCode()) {
            errln("Calendar hash code ignores lenient setting");
        }
        b.setLenient(a.isLenient());

        // Assume getTimeZone() returns a reference, not a clone
        // of a reference -- this is true as of this writing
        TimeZone atz = a.getTimeZone();
        TimeZone btz = b.getTimeZone();

        btz.setRawOffset(atz.getRawOffset() + 60 * 60 * 1000);
        if (atz.hashCode() == btz.hashCode()) {
            errln(atz.hashCode() + "==" + btz.hashCode());
        }
        if (a.getTimeZone() != b.getTimeZone() && a.hashCode() == b.hashCode()) {
            errln("Calendar hash code ignores zone");
        }
        b.getTimeZone().setRawOffset(a.getTimeZone().getRawOffset());

        GregorianCalendar c = new GregorianCalendar();
        GregorianCalendar d = c.clone();
        if (c.hashCode() != d.hashCode()) {
            errln("GregorianCalendar hash code unequal for clones objects");
        }
        Date cutover = c.getGregorianChange();
        d.setGregorianChange(new Date(cutover.getTime() + 24 * 60 * 60 * 1000));
        if (c.hashCode() == d.hashCode()) {
            errln("GregorianCalendar hash code ignores cutover");
        }
    }

    /** GregorianCalendar.equals() ignores cutover date */
    @Test
    public void Test4141665() {
        GregorianCalendar cal = new GregorianCalendar();
        GregorianCalendar cal2 = cal.clone();
        Date cut = cal.getGregorianChange();
        Date cut2 = new Date(cut.getTime() + 100 * 24 * 60 * 60 * 1000L); // 100 days
        // later
        if (!cal.equals(cal2)) {
            errln("Cloned GregorianCalendars not equal");
        }
        cal2.setGregorianChange(cut2);
        if (cal.equals(cal2)) {
            errln("GregorianCalendar.equals() ignores cutover");
        }
    }

    /**
     * Bug states that ArrayIndexOutOfBoundsException is thrown by GregorianCalendar.roll() when
     * IllegalArgumentException should be.
     */
    @Test
    public void Test4142933() {
        GregorianCalendar calendar = new GregorianCalendar();
        try {
            calendar.roll(-1, true);
            errln("Test failed, no exception trown");
        } catch (IllegalArgumentException e) {
            // OK: Do nothing
            // logln("Test passed");
            System.out.print("");
        } catch (Exception e) {
            errln("Test failed. Unexpected exception is thrown: " + e);
            e.printStackTrace();
        }
    }

    /**
     * GregorianCalendar handling of Dates Long.MIN_VALUE and Long.MAX_VALUE is confusing; unless
     * the time zone has a raw offset of zero, one or the other of these will wrap. We've modified
     * the test given in the bug report to therefore only check the behavior of a calendar with a
     * zero raw offset zone.
     */
    @Test
    public void Test4145158() {
        GregorianCalendar calendar = new GregorianCalendar();

        calendar.setTimeZone(TimeZone.getTimeZone("GMT"));

        calendar.setTime(new Date(Long.MIN_VALUE));
        int year1 = calendar.get(Calendar.YEAR);
        int era1 = calendar.get(Calendar.ERA);

        calendar.setTime(new Date(Long.MAX_VALUE));
        int year2 = calendar.get(Calendar.YEAR);
        int era2 = calendar.get(Calendar.ERA);

        if (year1 == year2 && era1 == era2) {
            errln("Fail: Long.MIN_VALUE or Long.MAX_VALUE wrapping around");
        }
    }

    /** Maximum value for YEAR field wrong. */
    @Test
    public void Test4145983() {
        GregorianCalendar calendar = new GregorianCalendar();
        calendar.setTimeZone(TimeZone.getTimeZone("GMT"));
        Date[] DATES = {new Date(Long.MAX_VALUE), new Date(Long.MIN_VALUE)};
        for (int i = 0; i < DATES.length; ++i) {
            calendar.setTime(DATES[i]);
            int year = calendar.get(Calendar.YEAR);
            int maxYear = calendar.getMaximum(Calendar.YEAR);
            if (year > maxYear) {
                errln(
                        "Failed for "
                                + DATES[i].getTime()
                                + " ms: year="
                                + year
                                + ", maxYear="
                                + maxYear);
            }
        }
    }

    /**
     * This is a bug in the validation code of GregorianCalendar. As reported, the bug seems worse
     * than it really is, due to a bug in the way the bug report test was written. In reality the
     * bug is restricted to the DAY_OF_YEAR field. - liu 6/29/98
     */
    @Test
    public void Test4147269() {
        GregorianCalendar calendar = new GregorianCalendar();
        calendar.setLenient(false);
        java.util.Calendar tempcal = java.util.Calendar.getInstance();
        tempcal.clear();
        tempcal.set(1996, Calendar.JANUARY, 3); // Arbitrary date
        Date date = tempcal.getTime();
        for (int field = 0; field < calendar.getFieldCount(); field++) {
            calendar.setTime(date);
            // Note: In the bug report, getActualMaximum() was called instead
            // of getMaximum() -- this was an error. The validation code doesn't
            // use getActualMaximum(), since that's too costly.
            int max = calendar.getMaximum(field);
            int value = max + 1;
            calendar.set(field, value);
            try {
                calendar.getTime(); // Force time computation
                // We expect an exception to be thrown. If we fall through
                // to the next line, then we have a bug.
                errln(
                        "Test failed with field "
                                + FIELD_NAME[field]
                                + ", date before: "
                                + date
                                + ", date after: "
                                + calendar.getTime()
                                + ", value: "
                                + value
                                + " (max = "
                                + max
                                + ")");
            } catch (IllegalArgumentException e) {
                System.out.print("");
            }
        }
    }

    /**
     * Reported bug is that a GregorianCalendar with a cutover of Date(Long.MAX_VALUE) doesn't
     * behave as a pure Julian calendar. CANNOT REPRODUCE THIS BUG
     */
    @Test
    public void Test4149677() {
        TimeZone[] zones = {
            TimeZone.getTimeZone("GMT"), TimeZone.getTimeZone("PST"), TimeZone.getTimeZone("EAT")
        };
        for (int i = 0; i < zones.length; ++i) {
            GregorianCalendar calendar = new GregorianCalendar(zones[i]);

            // Make sure extreme values don't wrap around
            calendar.setTime(new Date(Long.MIN_VALUE));
            if (calendar.get(Calendar.ERA) != GregorianCalendar.BC) {
                errln("Fail: Long.MIN_VALUE ms has an AD year");
            }
            calendar.setTime(new Date(Long.MAX_VALUE));
            if (calendar.get(Calendar.ERA) != GregorianCalendar.AD) {
                errln("Fail: Long.MAX_VALUE ms has a BC year");
            }

            calendar.setGregorianChange(new Date(Long.MAX_VALUE));
            // to obtain a pure Julian calendar

            boolean is100Leap = calendar.isLeapYear(100);
            if (!is100Leap) {
                errln("test failed with zone " + zones[i].getID());
                errln(" cutover date is Calendar.MAX_DATE");
                errln(" isLeapYear(100) returns: " + is100Leap);
            }
        }
    }

    /**
     * Calendar and Date HOUR broken. If HOUR is out-of-range, Calendar and Date classes will
     * misbehave.
     */
    @Test
    public void Test4162587() {
        TimeZone saveZone = TimeZone.getDefault();

        try {
            TimeZone tz = TimeZone.getTimeZone("PST");
            TimeZone.setDefault(tz);
            GregorianCalendar cal = new GregorianCalendar(tz);
            Date d;

            for (int i = 0; i < 5; ++i) {
                if (i > 0) logln("---");

                cal.clear();
                cal.set(1998, Calendar.APRIL, 5, i, 0);
                d = cal.getTime();
                String s0 = d.toString();
                logln("0 " + i + ": " + s0);

                cal.clear();
                cal.set(1998, Calendar.APRIL, 4, i + 24, 0);
                d = cal.getTime();
                String sPlus = d.toString();
                logln("+ " + i + ": " + sPlus);

                cal.clear();
                cal.set(1998, Calendar.APRIL, 6, i - 24, 0);
                d = cal.getTime();
                String sMinus = d.toString();
                logln("- " + i + ": " + sMinus);

                if (!s0.equals(sPlus) || !s0.equals(sMinus)) {
                    errln("Fail: All three lines must match");
                }
            }
        } finally {
            TimeZone.setDefault(saveZone);
        }
    }

    /** Adding 12 months behaves differently from adding 1 year */
    @Test
    public void Test4165343() {
        GregorianCalendar calendar = new GregorianCalendar(1996, Calendar.FEBRUARY, 29);
        Date start = calendar.getTime();
        logln("init date: " + start);
        calendar.add(Calendar.MONTH, 12);
        Date date1 = calendar.getTime();
        logln("after adding 12 months: " + date1);
        calendar.setTime(start);
        calendar.add(Calendar.YEAR, 1);
        Date date2 = calendar.getTime();
        logln("after adding one year : " + date2);
        if (date1.equals(date2)) {
            logln("Test passed");
        } else {
            errln("Test failed");
        }
    }

    /** GregorianCalendar.getActualMaximum() does not account for first day of week. */
    @Test
    public void Test4166109() {
        /*
         * Test month:
         *
         * March 1998 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
         * 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
         */
        boolean passed = true;
        int field = Calendar.WEEK_OF_MONTH;

        GregorianCalendar calendar = new GregorianCalendar(Locale.US);
        calendar.set(1998, Calendar.MARCH, 1);
        calendar.setMinimalDaysInFirstWeek(1);
        logln("Date:  " + calendar.getTime());

        int firstInMonth = calendar.get(Calendar.DAY_OF_MONTH);

        for (int firstInWeek = Calendar.SUNDAY; firstInWeek <= Calendar.SATURDAY; firstInWeek++) {
            calendar.setFirstDayOfWeek(firstInWeek);
            int returned = calendar.getActualMaximum(field);
            int expected = (31 + ((firstInMonth - firstInWeek + 7) % 7) + 6) / 7;

            logln(
                    "First day of week = "
                            + firstInWeek
                            + "  getActualMaximum(WEEK_OF_MONTH) = "
                            + returned
                            + "  expected = "
                            + expected
                            + ((returned == expected) ? "  ok" : "  FAIL"));

            if (returned != expected) {
                passed = false;
            }
        }
        if (!passed) {
            errln("Test failed");
        }
    }

    /** Calendar.getActualMaximum(YEAR) works wrong. */
    @Test
    public void Test4167060() {
        int field = Calendar.YEAR;
        DateFormat format = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy G", Locale.US);

        GregorianCalendar calendars[] = {
            new GregorianCalendar(100, Calendar.NOVEMBER, 1),
            new GregorianCalendar(-99 /* 100BC */, Calendar.JANUARY, 1),
            new GregorianCalendar(1996, Calendar.FEBRUARY, 29),
        };

        String[] id = {"Hybrid", "Gregorian", "Julian"};

        for (int k = 0; k < 3; ++k) {
            logln("--- " + id[k] + " ---");

            for (int j = 0; j < calendars.length; ++j) {
                GregorianCalendar calendar = calendars[j];
                if (k == 1) {
                    calendar.setGregorianChange(new Date(Long.MIN_VALUE));
                } else if (k == 2) {
                    calendar.setGregorianChange(new Date(Long.MAX_VALUE));
                }

                format.setCalendar(calendar.clone());

                Date dateBefore = calendar.getTime();

                int maxYear = calendar.getActualMaximum(field);
                logln("maxYear: " + maxYear + " for " + format.format(calendar.getTime()));
                logln("date before: " + format.format(dateBefore));

                int years[] = {2000, maxYear - 1, maxYear, maxYear + 1};

                for (int i = 0; i < years.length; i++) {
                    boolean valid = years[i] <= maxYear;
                    calendar.set(field, years[i]);
                    Date dateAfter = calendar.getTime();
                    int newYear = calendar.get(field);
                    calendar.setTime(dateBefore); // restore calendar for next
                    // use

                    logln(
                            " Year "
                                    + years[i]
                                    + (valid ? " ok " : " bad")
                                    + " => "
                                    + format.format(dateAfter));
                    if (valid && newYear != years[i]) {
                        errln(
                                "  FAIL: "
                                        + newYear
                                        + " should be valid; date, month and time shouldn't change");
                    } else if (!valid && newYear == years[i]) {
                        // We no longer require strict year maxima. That is, the
                        // calendar
                        // algorithm may work for values > the stated maximum.
                        // errln(" FAIL: " + newYear + " should be invalid");
                        logln("  Note: " + newYear + " > maximum, but still valid");
                    }
                }
            }
        }
    }

    /** Calendar.roll broken This bug relies on the TimeZone bug 4173604 to also be fixed. */
    @Test
    public void Test4173516() {
        int fieldsList[][] = {
            {1997, Calendar.FEBRUARY, 1, 10, 45, 15, 900},
            {1999, Calendar.DECEMBER, 22, 23, 59, 59, 999}
        };
        int limit = 40;
        GregorianCalendar cal = new GregorianCalendar();

        cal.setTime(new Date(0));
        cal.roll(Calendar.HOUR, 0x7F000000);
        cal.roll(Calendar.HOUR, -0x7F000000);
        if (cal.getTime().getTime() != 0) {
            errln("Hour rolling broken");
        }

        for (int op = 0; op < 2; ++op) {
            logln("Testing GregorianCalendar " + (op == 0 ? "add" : "roll"));
            for (int field = 0; field < cal.getFieldCount(); ++field) {
                if (field != Calendar.ZONE_OFFSET
                        && field != Calendar.DST_OFFSET
                        && field != Calendar.IS_LEAP_MONTH) {
                    for (int j = 0; j < fieldsList.length; ++j) {
                        int fields[] = fieldsList[j];
                        cal.clear();
                        cal.set(fields[0], fields[1], fields[2], fields[3], fields[4], fields[5]);
                        cal.set(Calendar.MILLISECOND, fields[6]);
                        cal.setMinimalDaysInFirstWeek(1);
                        for (int i = 0; i < 2 * limit; i++) {
                            if (op == 0) {
                                cal.add(field, i < limit ? 1 : -1);
                            } else {
                                cal.roll(field, i < limit ? 1 : -1);
                            }
                        }
                        if (cal.get(Calendar.YEAR) != fields[0]
                                || cal.get(Calendar.MONTH) != fields[1]
                                || cal.get(Calendar.DATE) != fields[2]
                                || cal.get(Calendar.HOUR_OF_DAY) != fields[3]
                                || cal.get(Calendar.MINUTE) != fields[4]
                                || cal.get(Calendar.SECOND) != fields[5]
                                || cal.get(Calendar.MILLISECOND) != fields[6]) {
                            errln(
                                    "Field "
                                            + field
                                            + " "
                                            + (op == 0 ? "add" : "roll")
                                            + " ("
                                            + FIELD_NAME[field]
                                            + ") FAIL, expected "
                                            + fields[0]
                                            + "/"
                                            + (fields[1] + 1)
                                            + "/"
                                            + fields[2]
                                            + " "
                                            + fields[3]
                                            + ":"
                                            + fields[4]
                                            + ":"
                                            + fields[5]
                                            + "."
                                            + fields[6]
                                            + ", got "
                                            + cal.get(Calendar.YEAR)
                                            + "/"
                                            + (cal.get(Calendar.MONTH) + 1)
                                            + "/"
                                            + cal.get(Calendar.DATE)
                                            + " "
                                            + cal.get(Calendar.HOUR_OF_DAY)
                                            + ":"
                                            + cal.get(Calendar.MINUTE)
                                            + ":"
                                            + cal.get(Calendar.SECOND)
                                            + "."
                                            + cal.get(Calendar.MILLISECOND));
                            cal.clear();
                            cal.set(
                                    fields[0], fields[1], fields[2], fields[3], fields[4],
                                    fields[5]);
                            cal.set(Calendar.MILLISECOND, fields[6]);
                            logln(
                                    "Start date: "
                                            + cal.get(Calendar.YEAR)
                                            + "/"
                                            + (cal.get(Calendar.MONTH) + 1)
                                            + "/"
                                            + cal.get(Calendar.DATE)
                                            + " "
                                            + cal.get(Calendar.HOUR_OF_DAY)
                                            + ":"
                                            + cal.get(Calendar.MINUTE)
                                            + ":"
                                            + cal.get(Calendar.SECOND)
                                            + "."
                                            + cal.get(Calendar.MILLISECOND));
                            long prev = cal.getTime().getTime();
                            for (int i = 0; i < 2 * limit; i++) {
                                if (op == 0) {
                                    cal.add(field, i < limit ? 1 : -1);
                                } else {
                                    cal.roll(field, i < limit ? 1 : -1);
                                }
                                long t = cal.getTime().getTime();
                                long delta = t - prev;
                                prev = t;
                                logln(
                                        (op == 0 ? "add(" : "roll(")
                                                + FIELD_NAME[field]
                                                + (i < limit ? ", +1) => " : ", -1) => ")
                                                + cal.get(Calendar.YEAR)
                                                + "/"
                                                + (cal.get(Calendar.MONTH) + 1)
                                                + "/"
                                                + cal.get(Calendar.DATE)
                                                + " "
                                                + cal.get(Calendar.HOUR_OF_DAY)
                                                + ":"
                                                + cal.get(Calendar.MINUTE)
                                                + ":"
                                                + cal.get(Calendar.SECOND)
                                                + "."
                                                + cal.get(Calendar.MILLISECOND)
                                                + " delta="
                                                + delta
                                                + " ms");
                            }
                        }
                    }
                }
            }
        }
    }

    @Test
    public void Test4174361() {
        GregorianCalendar calendar = new GregorianCalendar(1996, 1, 29);

        calendar.add(Calendar.MONTH, 10);
        // Date date1 = calendar.getTime();
        // date1 = null;
        int d1 = calendar.get(Calendar.DAY_OF_MONTH);

        calendar = new GregorianCalendar(1996, 1, 29);
        calendar.add(Calendar.MONTH, 11);
        // Date date2 = calendar.getTime();
        // date2 = null;
        int d2 = calendar.get(Calendar.DAY_OF_MONTH);

        if (d1 != d2) {
            errln("adding months to Feb 29 broken");
        }
    }

    /** Calendar does not update field values when setTimeZone is called. */
    @Test
    public void Test4177484() {
        TimeZone PST = TimeZone.getTimeZone("PST");
        TimeZone EST = TimeZone.getTimeZone("EST");

        Calendar cal = Calendar.getInstance(PST, Locale.US);
        cal.clear();
        cal.set(1999, 3, 21, 15, 5, 0); // Arbitrary
        int h1 = cal.get(Calendar.HOUR_OF_DAY);
        cal.setTimeZone(EST);
        int h2 = cal.get(Calendar.HOUR_OF_DAY);
        if (h1 == h2) {
            errln("FAIL: Fields not updated after setTimeZone");
        }

        // getTime() must NOT change when time zone is changed.
        // getTime() returns zone-independent time in ms.
        cal.clear();
        cal.setTimeZone(PST);
        cal.set(Calendar.HOUR_OF_DAY, 10);
        Date pst10 = cal.getTime();
        cal.setTimeZone(EST);
        Date est10 = cal.getTime();
        if (!pst10.equals(est10)) {
            errln("FAIL: setTimeZone changed time");
        }
    }

    /** Week of year is wrong at the start and end of the year. */
    @Test
    public void Test4197699() {
        GregorianCalendar cal = new GregorianCalendar();
        cal.setFirstDayOfWeek(Calendar.MONDAY);
        cal.setMinimalDaysInFirstWeek(4);
        DateFormat fmt = new SimpleDateFormat("E dd MMM yyyy  'DOY='D 'WOY='w");
        fmt.setCalendar(cal);

        int[] DATA = {
            2000, Calendar.JANUARY, 1, 52,
            2001, Calendar.DECEMBER, 31, 1,
        };

        for (int i = 0; i < DATA.length; ) {
            cal.set(DATA[i++], DATA[i++], DATA[i++]);
            int expWOY = DATA[i++];
            int actWOY = cal.get(Calendar.WEEK_OF_YEAR);
            if (expWOY == actWOY) {
                logln("Ok: " + fmt.format(cal.getTime()));
            } else {
                errln("FAIL: " + fmt.format(cal.getTime()) + ", expected WOY=" + expWOY);
                cal.add(Calendar.DATE, -8);
                for (int j = 0; j < 14; ++j) {
                    cal.add(Calendar.DATE, 1);
                    logln(fmt.format(cal.getTime()));
                }
            }
        }
    }

    /**
     * Calendar DAY_OF_WEEK_IN_MONTH fields->time broken. The problem is in the field disambiguation
     * code in GregorianCalendar. This code is supposed to choose the most recent set of fields
     * among the following:
     *
     * <p>MONTH + DAY_OF_MONTH MONTH + WEEK_OF_MONTH + DAY_OF_WEEK MONTH + DAY_OF_WEEK_IN_MONTH +
     * DAY_OF_WEEK DAY_OF_YEAR WEEK_OF_YEAR + DAY_OF_WEEK
     */
    @Test
    public void Test4209071() {
        Calendar cal = Calendar.getInstance(Locale.US);

        // General field setting test
        int Y = 1995;

        Date d[] = new Date[13];
        java.util.Calendar tempcal = java.util.Calendar.getInstance();
        tempcal.clear();
        tempcal.set(Y, Calendar.JANUARY, 1);
        d[0] = tempcal.getTime();
        tempcal.set(Y, Calendar.MARCH, 1);
        d[1] = tempcal.getTime();
        tempcal.set(Y, Calendar.JANUARY, 4);
        d[2] = tempcal.getTime();
        tempcal.set(Y, Calendar.JANUARY, 18);
        d[3] = tempcal.getTime();
        tempcal.set(Y, Calendar.JANUARY, 18);
        d[4] = tempcal.getTime();
        tempcal.set(Y - 1, Calendar.DECEMBER, 22);
        d[5] = tempcal.getTime();
        tempcal.set(Y, Calendar.JANUARY, 26);
        d[6] = tempcal.getTime();
        tempcal.set(Y, Calendar.JANUARY, 26);
        d[7] = tempcal.getTime();
        tempcal.set(Y, Calendar.MARCH, 1);
        d[8] = tempcal.getTime();
        tempcal.set(Y, Calendar.OCTOBER, 6);
        d[9] = tempcal.getTime();
        tempcal.set(Y, Calendar.OCTOBER, 13);
        d[10] = tempcal.getTime();
        tempcal.set(Y, Calendar.AUGUST, 10);
        d[11] = tempcal.getTime();
        tempcal.set(Y, Calendar.DECEMBER, 7);
        d[12] = tempcal.getTime();

        Object[] FIELD_DATA = {
            // Add new test cases as needed.

            // 0
            new int[] {},
            d[0],
            // 1
            new int[] {Calendar.MONTH, Calendar.MARCH},
            d[1],
            // 2
            new int[] {Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY},
            d[2],
            // 3
            new int[] {
                Calendar.DAY_OF_WEEK, Calendar.THURSDAY, Calendar.DAY_OF_MONTH, 18,
            },
            d[3],
            // 4
            new int[] {
                Calendar.DAY_OF_MONTH, 18, Calendar.DAY_OF_WEEK, Calendar.THURSDAY,
            },
            d[4],
            // 5 (WOM -1 is in previous month)
            new int[] {
                Calendar.DAY_OF_MONTH, 18,
                Calendar.WEEK_OF_MONTH, -1,
                Calendar.DAY_OF_WEEK, Calendar.THURSDAY,
            },
            d[5],
            // 6
            new int[] {
                Calendar.DAY_OF_MONTH, 18,
                Calendar.WEEK_OF_MONTH, 4,
                Calendar.DAY_OF_WEEK, Calendar.THURSDAY,
            },
            d[6],
            // 7 (DIM -1 is in same month)
            new int[] {
                Calendar.DAY_OF_MONTH, 18,
                Calendar.DAY_OF_WEEK_IN_MONTH, -1,
                Calendar.DAY_OF_WEEK, Calendar.THURSDAY,
            },
            d[7],
            // 8
            new int[] {
                Calendar.WEEK_OF_YEAR, 9, Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY,
            },
            d[8],
            // 9
            new int[] {
                Calendar.MONTH, Calendar.OCTOBER,
                Calendar.DAY_OF_WEEK_IN_MONTH, 1,
                Calendar.DAY_OF_WEEK, Calendar.FRIDAY,
            },
            d[9],
            // 10
            new int[] {
                Calendar.MONTH, Calendar.OCTOBER,
                Calendar.WEEK_OF_MONTH, 2,
                Calendar.DAY_OF_WEEK, Calendar.FRIDAY,
            },
            d[10],
            // 11
            new int[] {
                Calendar.MONTH, Calendar.OCTOBER,
                Calendar.DAY_OF_MONTH, 15,
                Calendar.DAY_OF_YEAR, 222,
            },
            d[11],
            // 12
            new int[] {
                Calendar.DAY_OF_WEEK, Calendar.THURSDAY,
                Calendar.MONTH, Calendar.DECEMBER,
            },
            d[12],
        };

        for (int i = 0; i < FIELD_DATA.length; i += 2) {
            int[] fields = (int[]) FIELD_DATA[i];
            Date exp = (Date) FIELD_DATA[i + 1];

            cal.clear();
            cal.set(Calendar.YEAR, Y);
            for (int j = 0; j < fields.length; j += 2) {
                cal.set(fields[j], fields[j + 1]);
            }

            Date act = cal.getTime();
            if (!act.equals(exp)) {
                errln(
                        "FAIL: Test "
                                + (i / 2)
                                + " got "
                                + act
                                + ", want "
                                + exp
                                + " (see test/java/util/Calendar/CalendarRegressionTest.java");
            }
        }

        tempcal.set(1997, Calendar.JANUARY, 5);
        d[0] = tempcal.getTime();
        tempcal.set(1997, Calendar.JANUARY, 26);
        d[1] = tempcal.getTime();
        tempcal.set(1997, Calendar.FEBRUARY, 23);
        d[2] = tempcal.getTime();
        tempcal.set(1997, Calendar.JANUARY, 26);
        d[3] = tempcal.getTime();
        tempcal.set(1997, Calendar.JANUARY, 5);
        d[4] = tempcal.getTime();
        tempcal.set(1996, Calendar.DECEMBER, 8);
        d[5] = tempcal.getTime();
        // Test specific failure reported in bug
        Object[] DATA = {
            1, d[0], 4, d[1],
            8, d[2], -1, d[3],
            -4, d[4], -8, d[5],
        };
        for (int i = 0; i < DATA.length; i += 2) {
            cal.clear();
            cal.set(Calendar.DAY_OF_WEEK_IN_MONTH, ((Number) DATA[i]).intValue());
            cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
            cal.set(Calendar.MONTH, Calendar.JANUARY);
            cal.set(Calendar.YEAR, 1997);
            Date actual = cal.getTime();
            if (!actual.equals(DATA[i + 1])) {
                errln(
                        "FAIL: Sunday "
                                + DATA[i]
                                + " of Jan 1997 -> "
                                + actual
                                + ", want "
                                + DATA[i + 1]);
            }
        }
    }

    /**
     * WEEK_OF_YEAR computed incorrectly. A failure of this test can indicate a problem in several
     * different places in the
     */
    @Test
    public void Test4288792() throws Exception {
        TimeZone savedTZ = TimeZone.getDefault();
        TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
        GregorianCalendar cal = new GregorianCalendar();

        for (int i = 1900; i < 2100; i++) {
            for (int j1 = 1; j1 <= 7; j1++) {
                // Loop for MinimalDaysInFirstWeek: 1..7
                for (int j = Calendar.SUNDAY; j <= Calendar.SATURDAY; j++) {
                    // Loop for FirstDayOfWeek: SUNDAY..SATURDAY
                    cal.clear();
                    cal.setMinimalDaysInFirstWeek(j1);
                    cal.setFirstDayOfWeek(j);
                    // Set the calendar to the first day of the last week
                    // of the year. This may overlap some of the start of
                    // the next year; that is, the last week of 1999 may
                    // include some of January 2000. Use the add() method
                    // to advance through the week. For each day, call
                    // get(WEEK_OF_YEAR). The result should be the same
                    // for the whole week. Note that a bug in
                    // getActualMaximum() will break this test.

                    // Set date to the mid year first before getActualMaximum(WEEK_OF_YEAR).
                    // getActualMaximum(WEEK_OF_YEAR) is based on the current calendar's
                    // year of week of year. After clear(), calendar is set to January 1st,
                    // which may belongs to previous year of week of year.
                    cal.set(i, Calendar.JULY, 1);
                    int maxWeek = cal.getActualMaximum(Calendar.WEEK_OF_YEAR);
                    cal.set(Calendar.WEEK_OF_YEAR, maxWeek);
                    cal.set(Calendar.DAY_OF_WEEK, j);
                    for (int k = 1; k < 7; k++) {
                        cal.add(Calendar.DATE, 1);
                        int WOY = cal.get(Calendar.WEEK_OF_YEAR);
                        if (WOY != maxWeek) {
                            errln(
                                    cal.getTime()
                                            + ",got="
                                            + WOY
                                            + ",expected="
                                            + maxWeek
                                            + ",min="
                                            + j1
                                            + ",first="
                                            + j);
                        }
                    }
                    // Now advance the calendar one more day. This should
                    // put it at the first day of week 1 of the next year.
                    cal.add(Calendar.DATE, 1);
                    int WOY = cal.get(Calendar.WEEK_OF_YEAR);
                    if (WOY != 1) {
                        errln(
                                cal.getTime()
                                        + ",got="
                                        + WOY
                                        + ",expected=1,min="
                                        + j1
                                        + ",first"
                                        + j);
                    }
                }
            }
        }
        TimeZone.setDefault(savedTZ);
    }

    /** Test fieldDifference(). */
    @Test
    public void TestJ438() throws Exception {
        int DATA[] = {
            2000, Calendar.JANUARY, 20, 2010, Calendar.JUNE, 15,
            2010, Calendar.JUNE, 15, 2000, Calendar.JANUARY, 20,
            1964, Calendar.SEPTEMBER, 7, 1999, Calendar.JUNE, 4,
            1999, Calendar.JUNE, 4, 1964, Calendar.SEPTEMBER, 7,
        };
        Calendar cal = Calendar.getInstance(Locale.US);
        for (int i = 0; i < DATA.length; i += 6) {
            int y1 = DATA[i];
            int m1 = DATA[i + 1];
            int d1 = DATA[i + 2];
            int y2 = DATA[i + 3];
            int m2 = DATA[i + 4];
            int d2 = DATA[i + 5];

            cal.clear();
            cal.set(y1, m1, d1);
            Date date1 = cal.getTime();
            cal.set(y2, m2, d2);
            Date date2 = cal.getTime();

            cal.setTime(date1);
            int dy = cal.fieldDifference(date2, Calendar.YEAR);
            int dm = cal.fieldDifference(date2, Calendar.MONTH);
            int dd = cal.fieldDifference(date2, Calendar.DATE);

            logln("" + date2 + " - " + date1 + " = " + dy + "y " + dm + "m " + dd + "d");

            cal.setTime(date1);
            cal.add(Calendar.YEAR, dy);
            cal.add(Calendar.MONTH, dm);
            cal.add(Calendar.DATE, dd);
            Date date22 = cal.getTime();
            if (!date2.equals(date22)) {
                errln(
                        "FAIL: " + date1 + " + " + dy + "y " + dm + "m " + dd + "d = " + date22
                                + ", exp " + date2);
            } else {
                logln("Ok: " + date1 + " + " + dy + "y " + dm + "m " + dd + "d = " + date22);
            }
        }
    }

    @Test
    public void TestT5555() throws Exception {
        Calendar cal = Calendar.getInstance();

        // Set date to Wednesday, February 21, 2007
        cal.set(2007, Calendar.FEBRUARY, 21);

        try {
            // Advance month by three years
            cal.add(Calendar.MONTH, 36);

            // Move to last Wednesday of month.
            cal.set(Calendar.DAY_OF_WEEK_IN_MONTH, -1);

            cal.getTime();
        } catch (Exception e) {
            errln("Got an exception calling getTime().");
        }

        int yy, mm, dd, ee;

        yy = cal.get(Calendar.YEAR);
        mm = cal.get(Calendar.MONTH);
        dd = cal.get(Calendar.DATE);
        ee = cal.get(Calendar.DAY_OF_WEEK_IN_MONTH);

        if (yy != 2010 || mm != Calendar.FEBRUARY || dd != 24 || ee != Calendar.WEDNESDAY) {
            errln("Got date " + yy + "/" + (mm + 1) + "/" + dd + ", expected 2010/2/24");
        }
    }

    /** Set behavior of DST_OFFSET field. ICU4J Jitterbug 9. */
    @Test
    public void TestJ9() {
        int HOURS = 60 * 60 * 1000;
        Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("PST"), Locale.US);

        final int END_FIELDS = 0x1234;

        int[] DATA = {
            // With no explicit ZONE/DST expect 12:00 am
            Calendar.MONTH,
            Calendar.JUNE,
            END_FIELDS,
            0,
            0, // expected hour, min

            // Normal ZONE/DST for June 1 Pacific is 8:00/1:00
            Calendar.MONTH,
            Calendar.JUNE,
            Calendar.ZONE_OFFSET,
            -8 * HOURS,
            Calendar.DST_OFFSET,
            HOURS,
            END_FIELDS,
            0,
            0, // expected hour, min

            // With ZONE/DST of 8:00/0:30 expect time of 12:30 am
            Calendar.MONTH,
            Calendar.JUNE,
            Calendar.ZONE_OFFSET,
            -8 * HOURS,
            Calendar.DST_OFFSET,
            HOURS / 2,
            END_FIELDS,
            0,
            30, // expected hour, min

            // With ZONE/DST of 8:00/UNSET expect time of 1:00 am
            Calendar.MONTH,
            Calendar.JUNE,
            Calendar.ZONE_OFFSET,
            -8 * HOURS,
            END_FIELDS,
            1,
            0, // expected hour, min

            // With ZONE/DST of UNSET/0:30 expect 4:30 pm (day before)
            Calendar.MONTH,
            Calendar.JUNE,
            Calendar.DST_OFFSET,
            HOURS / 2,
            END_FIELDS,
            16,
            30, // expected hour, min
        };

        for (int i = 0; i < DATA.length; ) {
            int start = i;
            cal.clear();

            // Set fields
            while (DATA[i] != END_FIELDS) {
                cal.set(DATA[i++], DATA[i++]);
            }
            ++i; // skip over END_FIELDS

            // Get hour/minute
            int h = cal.get(Calendar.HOUR_OF_DAY);
            int m = cal.get(Calendar.MINUTE);

            // Check
            if (h != DATA[i] || m != DATA[i + 1]) {
                errln(
                        "Fail: expected "
                                + DATA[i]
                                + ":"
                                + DATA[i + 1]
                                + ", got "
                                + h
                                + ":"
                                + m
                                + " after:");
                while (DATA[start] != END_FIELDS) {
                    logln("set(" + FIELD_NAME[DATA[start++]] + ", " + DATA[start++] + ");");
                }
            }

            i += 2; // skip over expected hour, min
        }
    }

    /**
     * DateFormat class mistakes date style and time style as follows: -
     * DateFormat.getDateTimeInstance takes date style as time style, and time style as date style -
     * If a Calendar is passed to DateFormat.getDateInstance, it returns time instance - If a
     * Calendar is passed to DateFormat.getTimeInstance, it returns date instance
     */
    @Test
    public void TestDateFormatFactoryJ26() {
        TimeZone zone = TimeZone.getDefault();
        try {
            Locale loc = Locale.US;
            TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
            java.util.Calendar tempcal = java.util.Calendar.getInstance();
            tempcal.set(2001, Calendar.APRIL, 5, 17, 43, 53);
            Date date = tempcal.getTime();
            Calendar cal = Calendar.getInstance(loc);
            Object[] DATA = {
                DateFormat.getDateInstance(DateFormat.SHORT, loc),
                "DateFormat.getDateInstance(DateFormat.SHORT, loc)",
                "4/5/01",
                DateFormat.getTimeInstance(DateFormat.SHORT, loc),
                "DateFormat.getTimeInstance(DateFormat.SHORT, loc)",
                "5:43\u202FPM",
                DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.SHORT, loc),
                "DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.SHORT, loc)",
                "Thursday, April 5, 2001 at 5:43\u202FPM",
                DateFormat.getDateInstance(cal, DateFormat.SHORT, loc),
                "DateFormat.getDateInstance(cal, DateFormat.SHORT, loc)",
                "4/5/01",
                DateFormat.getTimeInstance(cal, DateFormat.SHORT, loc),
                "DateFormat.getTimeInstance(cal, DateFormat.SHORT, loc)",
                "5:43\u202FPM",
                DateFormat.getDateTimeInstance(cal, DateFormat.FULL, DateFormat.SHORT, loc),
                "DateFormat.getDateTimeInstance(cal, DateFormat.FULL, DateFormat.SHORT, loc)",
                "Thursday, April 5, 2001 at 5:43\u202FPM",
                cal.getDateTimeFormat(DateFormat.SHORT, DateFormat.FULL, loc),
                "cal.getDateTimeFormat(DateFormat.SHORT, DateFormat.FULL, loc)",
                "4/5/01, 5:43:53\u202FPM Pacific Daylight Time",
                cal.getDateTimeFormat(DateFormat.FULL, DateFormat.SHORT, loc),
                "cal.getDateTimeFormat(DateFormat.FULL, DateFormat.SHORT, loc)",
                "Thursday, April 5, 2001 at 5:43\u202FPM",
            };
            for (int i = 0; i < DATA.length; i += 3) {
                DateFormat df = (DateFormat) DATA[i];
                String desc = (String) DATA[i + 1];
                String exp = (String) DATA[i + 2];
                String got = df.format(date);
                if (got.equals(exp)) {
                    logln("Ok: " + desc + " => " + got);
                } else {
                    errln("FAIL: " + desc + " => " + got + ", expected " + exp);
                }
            }
        } finally {
            TimeZone.setDefault(zone);
        }
    }

    @Test
    public void TestRegistration() {
        /*
         * Set names = Calendar.getCalendarFactoryNames();
         *
         * TimeZone tz = TimeZone.getDefault(); Locale loc =
         * Locale.getDefault(); Iterator iter = names.iterator(); while
         * (iter.hasNext()) { String name = (String)iter.next(); logln("Testing
         * factory: " + name);
         *
         * Calendar cal = Calendar.getInstance(tz, loc, name); logln("Calendar
         * class: " + cal.getClass());
         *
         * DateFormat fmt = cal.getDateTimeFormat(DateFormat.LONG,
         * DateFormat.LONG, loc);
         *
         * logln("Date: " + fmt.format(cal.getTime())); }
         *  // register new default for our locale logln("\nTesting
         * registration"); loc = new Locale("en", "US"); Object key =
         * Calendar.register(JapaneseCalendar.factory(), loc, true);
         *
         * loc = new Locale("en", "US", "TEST"); Calendar cal =
         * Calendar.getInstance(loc); logln("Calendar class: " +
         * cal.getClass()); DateFormat fmt =
         * cal.getDateTimeFormat(DateFormat.LONG, DateFormat.LONG, loc);
         * logln("Date: " + fmt.format(cal.getTime()));
         *  // force to use other default anyway logln("\nOverride
         * registration"); cal = Calendar.getInstance(tz, loc, "Gregorian"); fmt =
         * cal.getDateTimeFormat(DateFormat.LONG, DateFormat.LONG, loc);
         * logln("Date: " + fmt.format(cal.getTime()));
         *  // unregister default logln("\nUnregistration"); logln("Unregister
         * returned: " + Calendar.unregister(key)); cal =
         * Calendar.getInstance(tz, loc, "Gregorian"); fmt =
         * cal.getDateTimeFormat(DateFormat.LONG, DateFormat.LONG, loc);
         * logln("Date: " + fmt.format(cal.getTime()));
         */
    }

    /**
     * test serialize-and-modify.
     *
     * @throws ClassNotFoundException
     */
    @Test
    public void TestSerialization3474() {
        try {
            ByteArrayOutputStream icuStream = new ByteArrayOutputStream();

            logln("icu Calendar");

            com.ibm.icu.util.GregorianCalendar icuCalendar =
                    new com.ibm.icu.util.GregorianCalendar();

            icuCalendar.setTimeInMillis(1187912555931L);
            long expectMillis = 1187912520931L; // with seconds (not ms) cleared.

            logln("instantiated: " + icuCalendar);
            logln("getMillis: " + icuCalendar.getTimeInMillis());
            icuCalendar.set(com.ibm.icu.util.GregorianCalendar.SECOND, 0);
            logln("setSecond=0: " + icuCalendar);
            {
                long gotMillis = icuCalendar.getTimeInMillis();
                if (gotMillis != expectMillis) {
                    errln("expect millis " + expectMillis + " but got " + gotMillis);
                } else {
                    logln("getMillis: " + gotMillis);
                }
            }
            ObjectOutputStream icuOut = new ObjectOutputStream(icuStream);
            icuOut.writeObject(icuCalendar);
            icuOut.flush();
            icuOut.close();

            ObjectInputStream icuIn =
                    new ObjectInputStream(new ByteArrayInputStream(icuStream.toByteArray()));
            icuCalendar = null;
            icuCalendar = (com.ibm.icu.util.GregorianCalendar) icuIn.readObject();

            logln("serialized back in: " + icuCalendar);
            {
                long gotMillis = icuCalendar.getTimeInMillis();
                if (gotMillis != expectMillis) {
                    errln("expect millis " + expectMillis + " but got " + gotMillis);
                } else {
                    logln("getMillis: " + gotMillis);
                }
            }

            icuCalendar.set(com.ibm.icu.util.GregorianCalendar.SECOND, 0);

            logln("setSecond=0: " + icuCalendar);
            {
                long gotMillis = icuCalendar.getTimeInMillis();
                if (gotMillis != expectMillis) {
                    errln(
                            "expect millis "
                                    + expectMillis
                                    + " after stream and setSecond but got "
                                    + gotMillis);
                } else {
                    logln("getMillis after stream and setSecond: " + gotMillis);
                }
            }
        } catch (IOException e) {
            errln(e.toString());
            e.printStackTrace();
        } catch (ClassNotFoundException cnf) {
            errln(cnf.toString());
            cnf.printStackTrace();
        }

        // JDK works correctly, etc etc.
        //        ByteArrayOutputStream jdkStream = new ByteArrayOutputStream();

        //        logln("\nSUN Calendar");
        //
        //        java.util.GregorianCalendar sunCalendar =
        //            new java.util.GregorianCalendar();
        //
        //        logln("instanzieren: "+sunCalendar);
        //        logln("getMillis: "+sunCalendar.getTimeInMillis());
        //        sunCalendar.set(java.util.GregorianCalendar.SECOND, 0);
        //        logln("setSecond=0: "+sunCalendar);
        //        logln("getMillis: "+sunCalendar.getTimeInMillis());
        //
        //        ObjectOutputStream sunOut =
        //            new ObjectOutputStream(jdkStream);
        //        sunOut.writeObject(sunCalendar);
        //        sunOut.flush();
        //        sunOut.close();
        //
        //        ObjectInputStream sunIn =
        //            new ObjectInputStream(new ByteArrayInputStream(jdkStream.toByteArray()));
        //        sunCalendar = null;
        //        sunCalendar = (java.util.GregorianCalendar)sunIn.readObject();
        //
        //        logln("serialized: "+sunCalendar);
        //        logln("getMillis: "+sunCalendar.getTimeInMillis());
        //
        //        sunCalendar.set(java.util.GregorianCalendar.SECOND, 0);
        //        logln("setSecond=0: "+sunCalendar);
        //        logln("getMillis: "+sunCalendar.getTimeInMillis());

    }

    @Test
    public void TestYearJump3279() {
        final long time = 1041148800000L;
        Calendar c = new GregorianCalendar();
        DateFormat fmt =
                DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, Locale.US);

        c.setTimeInMillis(time);
        int year1 = c.get(Calendar.YEAR);

        logln("time: " + fmt.format(new Date(c.getTimeInMillis())));

        logln("setting DOW to " + c.getFirstDayOfWeek());
        c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek());
        logln("week: " + c.getTime());
        logln("week adjust: " + fmt.format(new Date(c.getTimeInMillis())));
        int year2 = c.get(Calendar.YEAR);

        if (year1 != year2) {
            errln("Error: adjusted day of week, and year jumped from " + year1 + " to " + year2);
        } else {
            logln("Year remained " + year2 + " - PASS.");
        }
    }

    @Test
    public void TestCalendarType6816() {
        Locale loc = new Locale("en", "TH");
        Calendar cal = Calendar.getInstance(loc);
        String calType = cal.getType();
        if (!calType.equals("buddhist")) {
            errln("FAIL: Calendar type for en_TH should still be buddhist");
        }
    }

    @Test
    public void TestGetKeywordValuesForLocale() {

        final String[][] PREFERRED = {
            {"root", "gregorian"},
            {"und", "gregorian"},
            {"en_US", "gregorian"},
            {"en_029", "gregorian"},
            {"th_TH", "buddhist", "gregorian"},
            {"und_TH", "buddhist", "gregorian"},
            {"en_TH", "buddhist", "gregorian"},
            {"he_IL", "gregorian", "hebrew", "islamic", "islamic-civil", "islamic-tbla"},
            {"ar_EG", "gregorian", "coptic", "islamic", "islamic-civil", "islamic-tbla"},
            {"ja", "gregorian", "japanese"},
            {"ps_Guru_IN", "gregorian", "indian"},
            {"th@calendar=gregorian", "buddhist", "gregorian"},
            {"en@calendar=islamic", "gregorian"},
            {"zh_TW", "gregorian", "roc", "chinese"},
            {"ar_IR", "persian", "gregorian", "islamic", "islamic-civil", "islamic-tbla"},
            {"th@rg=SAZZZZ", "gregorian", "islamic-umalqura", "islamic", "islamic-rgsa"},

            // tests for ICU-22364
            {"zh_CN@rg=TW", "gregorian", "chinese"}, // invalid subdivision code
            {
                "zh_CN@rg=TWzzzz", "gregorian", "roc", "chinese",
            }, // whole region
            {
                "zh_TW@rg=TWxxxx", "gregorian", "roc", "chinese"
            }, // invalid subdivision code (ignored)
            {"zh_TW@rg=ARa", "gregorian"}, // single-letter subdivision code
            {"zh_TW@rg=AT1", "gregorian"}, // single-digit subdivision code
            {"zh_TW@rg=USca", "gregorian"}, // two-letter subdivision code
            {"zh_TW@rg=IT53", "gregorian"}, // two-digit subdivision code
            {"zh_TW@rg=AUnsw", "gregorian"}, // three-letter subdivision code
            {"zh_TW@rg=EE130", "gregorian"}, // three-digit subdivision code
        };

        String[] ALL = Calendar.getKeywordValuesForLocale("calendar", ULocale.getDefault(), false);
        HashSet<String> ALLSET = new HashSet<>();
        for (int i = 0; i < ALL.length; i++) {
            if (ALL[i] == "unknown") {
                errln("Calendar.getKeywordValuesForLocale should not return \"unknown\"");
            }
            ALLSET.add(ALL[i]);
        }

        for (int i = 0; i < PREFERRED.length; i++) {
            ULocale loc = new ULocale(PREFERRED[i][0]);
            String[] expected = new String[PREFERRED[i].length - 1];
            System.arraycopy(PREFERRED[i], 1, expected, 0, expected.length);

            String[] pref = Calendar.getKeywordValuesForLocale("calendar", loc, true);
            boolean matchPref = false;
            if (pref.length == expected.length) {
                matchPref = true;
                for (int j = 0; j < pref.length; j++) {
                    if (!pref[j].equals(expected[j])) {
                        matchPref = false;
                    }
                }
            }
            if (!matchPref) {
                errln(
                        "FAIL: Preferred values for locale "
                                + loc
                                + " got:"
                                + Arrays.toString(pref)
                                + " expected:"
                                + Arrays.toString(expected));
            }

            String[] all = Calendar.getKeywordValuesForLocale("calendar", loc, false);
            boolean matchAll = false;
            if (all.length == ALLSET.size()) {
                matchAll = true;
                for (int j = 0; j < all.length; j++) {
                    if (!ALLSET.contains(all[j])) {
                        matchAll = false;
                        break;
                    }
                }
            }
            if (!matchAll) {
                errln("FAIL: All values for locale " + loc + " got:" + Arrays.toString(all));
            }
        }
    }

    @Test
    public void TestTimeStamp() {
        long start = 0, time;

        // Create a new Gregorian Calendar.
        Calendar cal = Calendar.getInstance(Locale.US);

        for (int i = 0; i < 20000; i++) {
            cal.set(2009, Calendar.JULY, 3, 0, 49, 46);

            time = cal.getTime().getTime();

            if (i == 0) {
                start = time;
            } else {
                if (start != time) {
                    errln("start and time not equal");
                    return;
                }
            }
        }
    }

    /*
     * Test case for add/roll with non-lenient calendar reported by ticket#8057.
     * Calendar#add may result internal fields out of valid range. ICU used to
     * trigger field range validation also for internal field changes triggered
     * by add/roll, then throws IllegalArgumentException. The field value range
     * validation should be done only for fields set by user code explicitly
     * in non-lenient mode.
     */
    @Test
    public void TestT8057() {
        // Set the calendar to the last day in a leap year
        GregorianCalendar cal = new GregorianCalendar();
        cal.setLenient(false);
        cal.clear();
        cal.set(2008, Calendar.DECEMBER, 31);

        // Force calculating then fields once.
        long t = cal.getTimeInMillis();

        long expected = 1262246400000L; // 2009-12-31 00:00 PST

        try {
            cal.add(Calendar.YEAR, 1);
            t = cal.getTimeInMillis();
            if (t != expected) {
                errln("FAIL: wrong date after add: expected=" + expected + " returned=" + t);
            }
        } catch (IllegalArgumentException e) {
            errln("FAIL: add method should not throw IllegalArgumentException");
        }
    }

    /*
     * Test case for ticket#8596.
     * Setting an year followed by getActualMaximum(Calendar.WEEK_OF_YEAR)
     * may result wrong maximum week.
     */
    @Test
    public void TestT8596() {
        GregorianCalendar gc = new GregorianCalendar(TimeZone.getTimeZone("Etc/GMT"));
        gc.setFirstDayOfWeek(Calendar.MONDAY);
        gc.setMinimalDaysInFirstWeek(4);

        // Force the calendar to resolve the fields once.
        // The maximum week number in 2011 is 52.
        gc.set(Calendar.YEAR, 2011);
        gc.get(Calendar.YEAR);

        // Set a date in year 2009, but not calling get to resolve
        // the calendar's internal field yet.
        gc.set(2009, Calendar.JULY, 1);

        // Then call getActuamMaximum for week of year.
        // #8596 was caused by conflict between year set
        // above and internal work calendar field resolution.
        int maxWeeks = gc.getActualMaximum(Calendar.WEEK_OF_YEAR);
        if (maxWeeks != 53) {
            errln("FAIL: Max week in 2009 in ISO calendar is 53, but got " + maxWeeks);
        }
    }

    /** Test case for ticket:9019 */
    @Test
    public void Test9019() {
        GregorianCalendar cal1 = new GregorianCalendar(TimeZone.GMT_ZONE, ULocale.US);
        GregorianCalendar cal2 = new GregorianCalendar(TimeZone.GMT_ZONE, ULocale.US);
        cal1.clear();
        cal2.clear();
        cal1.set(2011, Calendar.MAY, 06);
        cal2.set(2012, Calendar.JANUARY, 06);
        cal1.setLenient(false);
        cal1.add(Calendar.MONTH, 8);
        if (!cal1.getTime().equals(cal2.getTime())) {
            errln("Error: Calendar is " + cal1.getTime() + " but expected " + cal2.getTime());
        } else {
            logln("Pass: rolled calendar is " + cal1.getTime());
        }
    }

    /**
     * Test case for ticket 9452 Calendar addition fall onto the missing date - 2011-12-30 in Samoa
     */
    @Test
    public void TestT9452() {
        TimeZone samoaTZ = TimeZone.getTimeZone("Pacific/Apia");
        GregorianCalendar cal = new GregorianCalendar(samoaTZ);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ");
        sdf.setTimeZone(samoaTZ);

        // Set date to 2011-12-29 00:00
        cal.clear();
        cal.set(2011, Calendar.DECEMBER, 29, 0, 0, 0);

        Date d = cal.getTime();
        String dstr = sdf.format(d);
        logln("Initial date: " + dstr);

        // Add 1 day
        cal.add(Calendar.DATE, 1);
        d = cal.getTime();
        dstr = sdf.format(d);
        logln("+1 day: " + dstr);
        assertEquals("Add 1 day", "2011-12-31T00:00:00+14:00", dstr);

        // Subtract 1 day
        cal.add(Calendar.DATE, -1);
        d = cal.getTime();
        dstr = sdf.format(d);
        logln("-1 day: " + dstr);
        assertEquals("Subtract 1 day", "2011-12-29T00:00:00-10:00", dstr);
    }

    /**
     * Test case for ticket 9403 semantic API change when attempting to call setTimeInMillis(long)
     * with a value outside the bounds. In strict mode an IllegalIcuArgumentException will be thrown
     * In lenient mode the value will be pinned to the relative min/max
     */
    @Test
    public void TestT9403() {
        Calendar myCal = Calendar.getInstance();
        long dateBit1, dateBit2, testMillis = 0L;
        boolean missedException = true;

        testMillis = -184303902611600000L;
        logln("Testing invalid setMillis value in lienent mode - using value: " + testMillis);

        try {
            myCal.setTimeInMillis(testMillis);
        } catch (IllegalArgumentException e) {
            logln("Fail: detected as bad millis");
            missedException = false;
        }
        assertTrue("Fail: out of bound millis did not trigger exception!", missedException);
        dateBit1 = myCal.get(Calendar.MILLISECOND);
        assertNotEquals("Fail: millis not changed to MIN_MILLIS", testMillis, dateBit1);

        logln("Testing invalid setMillis value in strict mode - using value: " + testMillis);
        myCal.setLenient(false);
        try {
            myCal.setTimeInMillis(testMillis);
        } catch (IllegalArgumentException e) {
            logln("Pass: correctly detected bad millis");
            missedException = false;
        }
        dateBit1 = myCal.get(Calendar.DAY_OF_MONTH);
        dateBit2 = myCal.getTimeInMillis();
        assertFalse(
                "Fail: error in setMillis, allowed invalid value : "
                        + testMillis
                        + "...returned dayOfMonth : "
                        + dateBit1
                        + " millis : "
                        + dateBit2,
                missedException);
    }

    /**
     * Test case for ticket 9968 subparse fails to return an error indication when start pos is 0
     */
    @Test
    public void TestT9968() {
        SimpleDateFormat sdf0 = new SimpleDateFormat("-MMMM");
        ParsePosition pos0 = new ParsePosition(0);
        /* Date d0 = */ sdf0.parse("-September", pos0);
        logln("sdf0: " + pos0.getErrorIndex() + "/" + pos0.getIndex());
        assertTrue("Fail: failed a good test", pos0.getErrorIndex() == -1);

        SimpleDateFormat sdf1 = new SimpleDateFormat("-MMMM");
        ParsePosition pos1 = new ParsePosition(0);
        /* Date d1 = */ sdf1.parse("-????", pos1);
        logln("sdf1: " + pos1.getErrorIndex() + "/" + pos1.getIndex());
        assertTrue("Fail: failed to detect bad parse", pos1.getErrorIndex() == 1);

        SimpleDateFormat sdf2 = new SimpleDateFormat("MMMM");
        ParsePosition pos2 = new ParsePosition(0);
        /* Date d2 = */ sdf2.parse("????", pos2);
        logln("sdf2: " + pos2.getErrorIndex() + "/" + pos2.getIndex());
        assertTrue("Fail: failed to detect bad parse", pos2.getErrorIndex() == 0);
    }

    @Test
    public void TestWeekendData_10560() {
        final Calendar.WeekData worldWeekData = new Calendar.WeekData(2, 1, 7, 0, 1, 86400000);
        final Calendar.WeekData usWeekData = new Calendar.WeekData(1, 1, 7, 0, 1, 86400000);
        final Calendar.WeekData testWeekData = new Calendar.WeekData(1, 2, 3, 4, 5, 86400000);

        assertEquals("World", worldWeekData, Calendar.getWeekDataForRegion("001"));
        assertEquals(
                "Illegal code => world",
                Calendar.getWeekDataForRegion("001"),
                Calendar.getWeekDataForRegion("xx"));
        assertEquals(
                "FR = DE",
                Calendar.getWeekDataForRegion("FR"),
                Calendar.getWeekDataForRegion("DE"));
        assertNotEquals(
                "IN ≠ world",
                Calendar.getWeekDataForRegion("001"),
                Calendar.getWeekDataForRegion("IN"));
        assertNotEquals(
                "FR ≠ EG",
                Calendar.getWeekDataForRegion("FR"),
                Calendar.getWeekDataForRegion("EG"));

        Calendar aCalendar = Calendar.getInstance(Locale.US);
        assertEquals("US", usWeekData, aCalendar.getWeekData());
        Calendar rgusCalendar = Calendar.getInstance(new ULocale("hi_IN@rg=USzzzz"));
        assertEquals("IN@rg=US", usWeekData, rgusCalendar.getWeekData());

        aCalendar.setWeekData(testWeekData);
        assertEquals("Custom", testWeekData, aCalendar.getWeekData());
    }

    /**
     * Test case for Ticket 13080 DateFormat#getDateTimeInstance() with the desired calendar type
     * ignores the first parameter Calendar cal.
     */
    @Test
    public void TestT13080() {
        Calendar setCal = Calendar.getInstance();
        setCal.clear();
        setCal.set(Calendar.YEAR, 2017);
        setCal.set(Calendar.MONTH, 4);
        setCal.set(Calendar.DATE, 5);
        Calendar calArr[] = {
            Calendar.getInstance(),
            new IslamicCalendar(),
            new HebrewCalendar(),
            new GregorianCalendar()
        };

        String[] expectedFormat = {
            "Friday, May 5, 2017",
            "Friday, Shaʻban 8, 1438 AH",
            "Friday, 9 Iyar 5777",
            "Friday, May 5, 2017"
        };

        List<String> formattedDateList = new ArrayList<String>();
        for (Calendar cal : calArr) {
            cal.setTime(setCal.getTime());
            DateFormat format =
                    DateFormat.getDateInstance(cal, DateFormat.FULL, Locale.getDefault());
            formattedDateList.add(format.format(cal.getTime()));
        }

        String[] actualFormat = formattedDateList.toArray(new String[0]);
        assertEquals(
                "Fail: dateformat doesn't interpret calendar correctly",
                expectedFormat,
                actualFormat);
    }

    @Test
    public void TestTicket11632() {
        Calendar cal = Calendar.getInstance();
        cal.clear();
        cal.set(Calendar.HOUR, 596);
        // hour value set upto 596 lies within the integer range for millisecond calculations
        assertEquals(
                "Incorrect time for integer range milliseconds",
                "Sun Jan 25 20:00:00 PST 1970",
                cal.getTime().toString());
        cal.clear();
        //  hour value set above 596 lies outside the integer range for millisecond calculations.
        // This will invoke
        // the long version of the compute millis in day method in the ICU internal API
        cal.set(Calendar.HOUR, 597);
        assertEquals(
                "Incorrect time for long range milliseconds",
                "Sun Jan 25 21:00:00 PST 1970",
                cal.getTime().toString());
        cal.clear();
        cal.set(Calendar.HOUR, 597);
        cal.set(Calendar.MINUTE, 60 * 24);
        assertEquals(
                "Incorrect time for long range milliseconds",
                "Mon Jan 26 21:00:00 PST 1970",
                cal.getTime().toString());
        cal.clear();
        cal.set(Calendar.HOUR_OF_DAY, 597);
        assertEquals(
                "Incorrect time for long range milliseconds",
                "Sun Jan 25 21:00:00 PST 1970",
                cal.getTime().toString());
    }

    @Test
    public void TestPersianCalOverflow() {
        String localeID = "bs_Cyrl@calendar=persian";
        Calendar cal = Calendar.getInstance(new ULocale(localeID));
        int maxMonth = cal.getMaximum(Calendar.MONTH);
        int maxDayOfMonth = cal.getMaximum(Calendar.DATE);
        int jd, month, dayOfMonth;
        for (jd = 67023580;
                jd <= 67023584;
                jd++) { // year 178171, int32_t overflow if jd >= 67023582
            cal.clear();
            cal.set(Calendar.JULIAN_DAY, jd);
            month = cal.get(Calendar.MONTH);
            dayOfMonth = cal.get(Calendar.DATE);
            if (month > maxMonth || dayOfMonth > maxDayOfMonth) {
                errln(
                        "Error: localeID "
                                + localeID
                                + ", julianDay "
                                + jd
                                + "; maxMonth "
                                + maxMonth
                                + ", got month "
                                + month
                                + "; maxDayOfMonth "
                                + maxDayOfMonth
                                + ", got dayOfMonth "
                                + dayOfMonth);
            }
        }
    }

    @Test
    public void TestIslamicCalOverflow() {
        String localeID = "ar@calendar=islamic-civil";
        Calendar cal = Calendar.getInstance(new ULocale(localeID));
        int maxMonth = cal.getMaximum(Calendar.MONTH);
        int maxDayOfMonth = cal.getMaximum(Calendar.DATE);
        int jd, year, month, dayOfMonth;
        for (jd = 73530872;
                jd <= 73530876;
                jd++) { // year 202002, int32_t overflow if jd >= 73530874
            cal.clear();
            cal.set(Calendar.JULIAN_DAY, jd);
            year = cal.get(Calendar.YEAR);
            month = cal.get(Calendar.MONTH);
            dayOfMonth = cal.get(Calendar.DATE);
            if (month > maxMonth || dayOfMonth > maxDayOfMonth) {
                errln(
                        "Error: localeID "
                                + localeID
                                + ", julianDay "
                                + jd
                                + "; got year "
                                + year
                                + "; maxMonth "
                                + maxMonth
                                + ", got month "
                                + month
                                + "; maxDayOfMonth "
                                + maxDayOfMonth
                                + ", got dayOfMonth "
                                + dayOfMonth);
            }
        }
    }

    @Test
    public void TestWeekOfYear13548() {
        int year = 2000;

        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.WEEK_OF_YEAR, 4);

        int resultYear = cal.get(Calendar.YEAR);
        if (year != resultYear) {
            errln("Fail: Expected year=" + year + ", actual=" + resultYear);
        }
    }

    @Test
    public void TestTimeZoneInLocale20465() {
        String TESTS[][] = {
            {"en-u-tz-usden", "America/Denver", "gregorian"},
            {"es-u-tz-usden", "America/Denver", "gregorian"},
            {"ms-u-tz-mykul", "Asia/Kuala_Lumpur", "gregorian"},
            {"zh-u-tz-mykul", "Asia/Kuala_Lumpur", "gregorian"},
            {"fr-u-ca-buddhist-tz-phmnl", "Asia/Manila", "buddhist"},
            {"th-u-ca-chinese-tz-gblon", "Europe/London", "chinese"},
            {"de-u-ca-coptic-tz-ciabj", "Africa/Abidjan", "coptic"},
            {"ja-u-ca-dangi-tz-hkhkg", "Asia/Hong_Kong", "dangi"},
            {"da-u-ca-ethioaa-tz-ruunera", "Asia/Ust-Nera", "ethiopic-amete-alem"},
            {"ko-u-ca-ethiopic-tz-cvrai", "Atlantic/Cape_Verde", "ethiopic"},
            {"fil-u-ca-gregory-tz-aubne", "Australia/Brisbane", "gregorian"},
            {"fa-u-ca-hebrew-tz-brrbr", "America/Rio_Branco", "hebrew"},
            {"gr-u-ca-indian-tz-lccas", "America/St_Lucia", "indian"},
            {"or-u-ca-islamic-tz-cayyn", "America/Swift_Current", "islamic"},
            {"my-u-ca-islamic-umalqura-tz-kzala", "Asia/Almaty", "islamic-umalqura"},
            {"lo-u-ca-islamic-tbla-tz-bmbda", "Atlantic/Bermuda", "islamic-tbla"},
            {"km-u-ca-islamic-civil-tz-aqplm", "Antarctica/Palmer", "islamic-civil"},
            {"kk-u-ca-islamic-rgsa-tz-usanc", "America/Anchorage", "islamic"},
            {"ar-u-ca-iso8601-tz-bjptn", "Africa/Porto-Novo", "gregorian"},
            {"he-u-ca-japanese-tz-tzdar", "Africa/Dar_es_Salaam", "japanese"},
            {"bs-u-ca-persian-tz-etadd", "Africa/Addis_Ababa", "persian"},
            {"it-u-ca-roc-tz-aruaq", "America/Argentina/San_Juan", "roc"},
        };
        TimeZone other = TimeZone.getTimeZone("America/Louisville");
        for (int i = 0; i < TESTS.length; ++i) {
            ULocale ulocale = new ULocale(TESTS[i][0]);
            Locale locale = new Locale(TESTS[i][0]);
            Calendar cal = Calendar.getInstance(locale);
            assertEquals(
                    "TimeZone from Calendar.getInstance(Locale loc=\"" + TESTS[i][0] + "\")",
                    TESTS[i][1],
                    cal.getTimeZone().getID());
            assertEquals(
                    "Calendar from Calendar.getInstance(Locale loc=\"" + TESTS[i][0] + "\")",
                    TESTS[i][2],
                    cal.getType());

            cal = Calendar.getInstance(ulocale);
            assertEquals(
                    "TimeZone from Calendar.getInstance(ULocale loc=\"" + TESTS[i][0] + "\")",
                    TESTS[i][1],
                    cal.getTimeZone().getID());
            assertEquals(
                    "Calendar from Calendar.getInstance(ULocale loc=\"" + TESTS[i][0] + "\")",
                    TESTS[i][2],
                    cal.getType());

            cal = Calendar.getInstance(other, locale);
            assertEquals(
                    "TimeZone from Calendar.getInstance(TimeZone zone=\"uslui\", Locale loc=\""
                            + TESTS[i][0]
                            + "\")",
                    other.getID(),
                    cal.getTimeZone().getID());
            assertEquals(
                    "Calendar from Calendar.getInstance(TimeZone zone=\"uslui\", Locale loc=\""
                            + TESTS[i][0]
                            + "\")",
                    TESTS[i][2],
                    cal.getType());

            cal = Calendar.getInstance(other, ulocale);
            assertEquals(
                    "TimeZone from Calendar.getInstance(TimeZone zone=\"uslui\", ULocale loc=\""
                            + TESTS[i][0]
                            + "\")",
                    other.getID(),
                    cal.getTimeZone().getID());
            assertEquals(
                    "Calendar from Calendar.getInstance(TimeZone zone=\"uslui\", ULocale loc=\""
                            + TESTS[i][0]
                            + "\")",
                    TESTS[i][2],
                    cal.getType());
        }
    }

    void VerifyNoAssertWithSetGregorianChange(String timezone) {
        TimeZone zone = TimeZone.getTimeZone(timezone);
        GregorianCalendar cal = new GregorianCalendar(zone, Locale.ENGLISH);
        cal.setTime(new Date());
        // The beginning of ECMAScript time, namely -(2**53)
        long startOfTime = -9007199254740992L;

        cal.setGregorianChange(new Date(startOfTime));
        cal.get(Calendar.ZONE_OFFSET);
        cal.get(Calendar.DST_OFFSET);
    }

    @Test
    public void TestAsiaManilaAfterSetGregorianChange22043() {
        VerifyNoAssertWithSetGregorianChange("Asia/Manila");
        for (String id : TimeZone.getAvailableIDs()) {
            VerifyNoAssertWithSetGregorianChange(id);
        }
    }

    @Test
    public void TestRespectUExtensionFw() { // ICU-22226
        String[] localeIds = {
            "en-US",
            "en-US-u-fw-xyz",
            "en-US-u-fw-sun",
            "en-US-u-fw-mon",
            "en-US-u-fw-thu",
            "en-US-u-fw-sat",
            // ICU-22434
            "en-US-u-ca-iso8601-fw-sun",
            "en-US-u-ca-iso8601-fw-mon",
            "en-US-u-ca-iso8601-fw-tue",
            "en-US-u-ca-iso8601-fw-wed",
            "en-US-u-ca-iso8601-fw-thu",
            "en-US-u-ca-iso8601-fw-fri",
            "en-US-u-ca-iso8601-fw-sat",
        };
        int[] expectedValues = {
            Calendar.SUNDAY,
            Calendar.SUNDAY,
            Calendar.SUNDAY,
            Calendar.MONDAY,
            Calendar.THURSDAY,
            Calendar.SATURDAY,
            // ICU-22434
            Calendar.SUNDAY,
            Calendar.MONDAY,
            Calendar.TUESDAY,
            Calendar.WEDNESDAY,
            Calendar.THURSDAY,
            Calendar.FRIDAY,
            Calendar.SATURDAY,
        };

        assertEquals(
                "The localeIds count matches the expectedValues count",
                localeIds.length,
                expectedValues.length);

        for (int i = 0; i < localeIds.length; i++) {
            assertEquals(
                    "Calendar.getFirstDayOfWeek() does not seem to respect fw extension u in locale id "
                            + localeIds[i],
                    expectedValues[i],
                    Calendar.getInstance(Locale.forLanguageTag(localeIds[i])).getFirstDayOfWeek());
        }
    }

    @Test
    public void TestIslamicUmalquraCalendarSlow() { // ICU-22513
        Locale loc = new Locale("th@calendar=islamic-umalqura");
        Calendar cal = Calendar.getInstance(loc);
        cal.clear();
        cal.add(Calendar.YEAR, 1229080905);
        cal.roll(Calendar.WEEK_OF_MONTH, 1499050699);
        cal.fieldDifference(new Date(0), Calendar.YEAR_WOY);
    }

    @Test
    public void TestMaxActualLimitsWithoutGet23006() {
        Calendar calendar = Calendar.getInstance(new Locale("zh_zh@calendar=chinese"));
        // set day equal to 8th August 2025 in Gregorian calendar
        // this is a leap month in Chinese calendar
        GregorianCalendar gc = new GregorianCalendar(TimeZone.GMT_ZONE);
        gc.clear();
        gc.set(2025, Calendar.AUGUST, 8);
        calendar.setTimeInMillis(gc.getTimeInMillis());
        int actualMaximumBeforeCallingGet = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        assertTrue(
                "get(ERA)",
                calendar.get(Calendar.ERA) > 0); // calling get will cause to compute fields
        int actualMaximumAfterCallingGet = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        assertEquals(
                "calling getActualMaximum before/after calling get should be the same",
                actualMaximumBeforeCallingGet,
                actualMaximumAfterCallingGet);
        assertEquals(
                "calling getActualMaximum before should return 29",
                29,
                actualMaximumBeforeCallingGet);

        gc.set(2026, Calendar.AUGUST, 8);
        calendar.setTimeInMillis(gc.getTimeInMillis());
        actualMaximumBeforeCallingGet = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        assertTrue(
                "get(ERA)",
                calendar.get(Calendar.ERA) > 0); // calling get will cause to compute fields
        actualMaximumAfterCallingGet = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
        assertEquals(
                "calling getActualMaximum before/after calling get should be the same",
                actualMaximumBeforeCallingGet,
                actualMaximumAfterCallingGet);
        assertEquals(
                "calling getActualMaximum before should return 30",
                30,
                actualMaximumBeforeCallingGet);
    }
}
// eof
